sfxState.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "sfx/sfxState.h"
  23. #include "sfx/sfxTypes.h"
  24. #include "core/stream/bitStream.h"
  25. #include "console/engineAPI.h"
  26. //#define DEBUG_SPEW
  27. IMPLEMENT_CO_DATABLOCK_V1( SFXState );
  28. ConsoleDocClass( SFXState,
  29. "@brief A boolean switch used to modify playlist behavior.\n\n"
  30. "Sound system states are used to allow playlist controllers to make decisions based on global state. This is useful, for "
  31. "example, to couple audio playback to gameplay state. Certain states may, for example, represent different locations that the "
  32. "listener can be in, like underwater, in open space, or indoors. Other states could represent moods of the current gameplay "
  33. "situation, like, for example, an aggressive mood during combat.\n\n"
  34. "By activating and deactivating sound states according to gameplay state, a set of concurrently running playlists may "
  35. "react and adapt to changes in the game.\n\n"
  36. "@section SFXState_activation Activation and Deactivation\n"
  37. "At any time, a given state can be either active or inactive. Calling activate() on a state increases an internal "
  38. "counter and calling deactivate() decreases the counter. Only when the count reaches zero will the state be "
  39. "deactivated.\n\n"
  40. "In addition to the activation count, states also maintain a disabling count. Calling disable() increases this count "
  41. "and calling enable() decreases it. As long as this count is greater than zero, a given state will not be activated "
  42. "even if its activation count is non-zero. Calling disable() on an active state will not only increase the disabling "
  43. "count but also deactivate the state. Calling enable() on a state with a positive activation count will re-activate "
  44. "the state when the disabling count reaches zero.\n\n"
  45. "@section SFXState_dependencies State Dependencies\n"
  46. "By listing other states in in its #includedStates and #excludedStates fields, a state may automatically trigger the "
  47. "activation or disabling of other states in the sytem when it is activated. This allows to form dependency chains "
  48. "between individual states.\n\n"
  49. "@tsexample\n"
  50. "// State indicating that the listener is submerged.\n"
  51. "singleton SFXState( AudioLocationUnderwater )\n"
  52. "{\n"
  53. " parentGroup = AudioLocation;\n"
  54. " // AudioStateExclusive is a class defined in the core scripts that will automatically\n"
  55. " // ensure for a state to deactivate all the sibling SFXStates in its parentGroup when it\n"
  56. " // is activated.\n"
  57. " className = \"AudioStateExclusive\";\n"
  58. "};\n"
  59. "\n"
  60. "// State suitable e.g. for combat.\n"
  61. "singleton SFXState( AudioMoodAggressive )\n"
  62. "{\n"
  63. " parentGroup = AudioMood;\n"
  64. " className = \"AudioStateExclusive\";\n"
  65. "};\n"
  66. "@endtsexample\n\n"
  67. "@see SFXPlayList\n"
  68. "@see SFXController\n"
  69. "@see SFXPlayList::state\n"
  70. "@see SFXPlayList::stateMode\n\n"
  71. "@ref SFX_interactive\n\n"
  72. "@ingroup SFX\n"
  73. "@ingroup Datablocks"
  74. );
  75. IMPLEMENT_CALLBACK( SFXState, onActivate, void, (), (),
  76. "Called when the state goes from inactive to active." );
  77. IMPLEMENT_CALLBACK( SFXState, onDeactivate, void, (), (),
  78. "called when the state goes from active to deactive." );
  79. static Vector< SFXState* > sgActiveStates( __FILE__, __LINE__ );
  80. //-----------------------------------------------------------------------------
  81. SFXState::SFXState()
  82. : mActiveCount( 0 ),
  83. mDisableCount( 0 )
  84. {
  85. dMemset( mIncludedStates, 0, sizeof( mIncludedStates ) );
  86. dMemset( mExcludedStates, 0, sizeof( mExcludedStates ) );
  87. }
  88. //-----------------------------------------------------------------------------
  89. void SFXState::initPersistFields()
  90. {
  91. docsURL;
  92. addGroup( "State" );
  93. addField( "includedStates", TypeSFXStateName, Offset( mIncludedStates, SFXState ),
  94. MaxIncludedStates,
  95. "States that will automatically be activated when this state is activated.\n\n"
  96. "@ref SFXState_activation" );
  97. addField( "excludedStates", TypeSFXStateName, Offset( mExcludedStates, SFXState ),
  98. MaxExcludedStates,
  99. "States that will automatically be disabled when this state is activated.\n\n"
  100. "@ref SFXState_activation" );
  101. endGroup( "State" );
  102. Parent::initPersistFields();
  103. }
  104. //-----------------------------------------------------------------------------
  105. void SFXState::activate()
  106. {
  107. mActiveCount ++;
  108. if( mActiveCount == 1 && !isDisabled() )
  109. _onActivate();
  110. }
  111. //-----------------------------------------------------------------------------
  112. void SFXState::deactivate()
  113. {
  114. if( !mActiveCount )
  115. return;
  116. mActiveCount --;
  117. if( !mActiveCount && !isDisabled() )
  118. _onDeactivate();
  119. }
  120. //-----------------------------------------------------------------------------
  121. void SFXState::enable()
  122. {
  123. if( !mDisableCount )
  124. return;
  125. mDisableCount --;
  126. if( !mDisableCount && mActiveCount > 0 )
  127. _onActivate();
  128. }
  129. //-----------------------------------------------------------------------------
  130. void SFXState::disable()
  131. {
  132. mDisableCount ++;
  133. if( mDisableCount == 1 && mActiveCount > 0 )
  134. _onDeactivate();
  135. }
  136. //-----------------------------------------------------------------------------
  137. bool SFXState::onAdd()
  138. {
  139. if( !Parent::onAdd() )
  140. return false;
  141. Sim::getSFXStateSet()->addObject( this );
  142. return true;
  143. }
  144. //-----------------------------------------------------------------------------
  145. bool SFXState::preload( bool server, String& errorStr )
  146. {
  147. if( !Parent::preload( server, errorStr ) )
  148. return false;
  149. if( !server )
  150. {
  151. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  152. if( !sfxResolve( &mIncludedStates[ i ], errorStr ) )
  153. return false;
  154. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  155. if( !sfxResolve( &mExcludedStates[ i ], errorStr ) )
  156. return false;
  157. }
  158. return true;
  159. }
  160. //-----------------------------------------------------------------------------
  161. void SFXState::packData( BitStream* stream )
  162. {
  163. Parent::packData( stream );
  164. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  165. sfxWrite( stream, mIncludedStates[ i ] );
  166. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  167. sfxWrite( stream, mExcludedStates[ i ] );
  168. }
  169. //-----------------------------------------------------------------------------
  170. void SFXState::unpackData( BitStream* stream )
  171. {
  172. Parent::unpackData( stream );
  173. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  174. sfxRead( stream, &mIncludedStates[ i ] );
  175. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  176. sfxRead( stream, &mExcludedStates[ i ] );
  177. }
  178. //-----------------------------------------------------------------------------
  179. void SFXState::_onActivate()
  180. {
  181. #ifdef DEBUG_SPEW
  182. Platform::outputDebugString( "[SFXState] Activating '%s'", getName() );
  183. #endif
  184. onActivate_callback();
  185. // Add the state to the list.
  186. sgActiveStates.push_back( this );
  187. // Activate included states.
  188. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  189. if( mIncludedStates[ i ] )
  190. mIncludedStates[ i ]->activate();
  191. // Disable excluded states.
  192. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  193. if( mExcludedStates[ i ] )
  194. mExcludedStates[ i ]->disable();
  195. }
  196. //-----------------------------------------------------------------------------
  197. void SFXState::_onDeactivate()
  198. {
  199. #ifdef DEBUG_SPEW
  200. Platform::outputDebugString( "[SFXState] Deactivating '%s'", getName() );
  201. #endif
  202. onDeactivate_callback();
  203. // Remove the state from the list.
  204. for( U32 i = 0; i < sgActiveStates.size(); ++ i )
  205. if( sgActiveStates[ i ] == this )
  206. {
  207. sgActiveStates.erase_fast( i );
  208. break;
  209. }
  210. // Deactivate included states.
  211. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  212. if( mIncludedStates[ i ] )
  213. mIncludedStates[ i ]->deactivate();
  214. // Enable excluded states.
  215. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  216. if( mExcludedStates[ i ] )
  217. mExcludedStates[ i ]->enable();
  218. }
  219. //=============================================================================
  220. // Console Methods.
  221. //=============================================================================
  222. // MARK: ---- Console Methods ----
  223. //-----------------------------------------------------------------------------
  224. DefineEngineMethod( SFXState, isActive, bool, (),,
  225. "Test whether the state is currently active.\n"
  226. "This is true when the activation count is >0 and the disabling count is =0.\n"
  227. "@return True if the state is currently active.\n"
  228. "@see activate" )
  229. {
  230. return object->isActive();
  231. }
  232. //-----------------------------------------------------------------------------
  233. DefineEngineMethod( SFXState, activate, void, (),,
  234. "Increase the activation count on the state.\n"
  235. "If the state isn't already active and it is not disabled, the state will be activated.\n"
  236. "@see isActive\n"
  237. "@see deactivate\n" )
  238. {
  239. object->activate();
  240. }
  241. //-----------------------------------------------------------------------------
  242. DefineEngineMethod( SFXState, deactivate, void, (),,
  243. "Decrease the activation count on the state.\n"
  244. "If the count reaches zero and the state was not disabled, the state will be deactivated.\n"
  245. "@see isActive\n"
  246. "@see activate\n" )
  247. {
  248. object->deactivate();
  249. }
  250. //-----------------------------------------------------------------------------
  251. DefineEngineMethod( SFXState, isDisabled, bool, (),,
  252. "Test whether the state is currently disabled.\n"
  253. "This is true when the disabling count of the state is non-zero.\n"
  254. "@return True if the state is disabled.\n\n"
  255. "@see disable\n" )
  256. {
  257. return object->isDisabled();
  258. }
  259. //-----------------------------------------------------------------------------
  260. DefineEngineMethod( SFXState, disable, void, (),,
  261. "Increase the disabling count of the state.\n"
  262. "If the state is currently active, it will be deactivated.\n"
  263. "@see isDisabled\n" )
  264. {
  265. object->disable();
  266. }
  267. //-----------------------------------------------------------------------------
  268. DefineEngineMethod( SFXState, enable, void, (),,
  269. "Decrease the disabling count of the state.\n"
  270. "If the disabling count reaches zero while the activation count is still non-zero, "
  271. "the state will be reactivated again.\n"
  272. "@see isDisabled\n" )
  273. {
  274. object->enable();
  275. }
  276. //=============================================================================
  277. // Console Functions.
  278. //=============================================================================
  279. // MARK: ---- Console Functions ----
  280. //-----------------------------------------------------------------------------
  281. DefineEngineFunction( sfxGetActiveStates, const char*, (),,
  282. "Return a newline-separated list of all active states.\n"
  283. "@return A list of the form\n"
  284. "@verbatim\n"
  285. "stateName1 NL stateName2 NL stateName3 ...\n"
  286. "@endverbatim\n"
  287. "where each element is the name of an active state object.\n\n"
  288. "@tsexample\n"
  289. "// Disable all active states.\n"
  290. "foreach$( %state in sfxGetActiveStates() )\n"
  291. " %state.disable();\n"
  292. "@endtsexample\n\n"
  293. "@ingroup SFX" )
  294. {
  295. StringBuilder str;
  296. bool isFirst = true;
  297. for( U32 i = 0; i < sgActiveStates.size(); ++ i )
  298. {
  299. if( !isFirst )
  300. str.append( ' ' );
  301. str.append( sgActiveStates[ i ]->getName() );
  302. isFirst = false;
  303. }
  304. return Con::getReturnBuffer( str );
  305. }