sfxState.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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. addGroup( "State" );
  92. addField( "includedStates", TypeSFXStateName, Offset( mIncludedStates, SFXState ),
  93. MaxIncludedStates,
  94. "States that will automatically be activated when this state is activated.\n\n"
  95. "@ref SFXState_activation" );
  96. addField( "excludedStates", TypeSFXStateName, Offset( mExcludedStates, SFXState ),
  97. MaxExcludedStates,
  98. "States that will automatically be disabled when this state is activated.\n\n"
  99. "@ref SFXState_activation" );
  100. endGroup( "State" );
  101. Parent::initPersistFields();
  102. }
  103. //-----------------------------------------------------------------------------
  104. void SFXState::activate()
  105. {
  106. mActiveCount ++;
  107. if( mActiveCount == 1 && !isDisabled() )
  108. _onActivate();
  109. }
  110. //-----------------------------------------------------------------------------
  111. void SFXState::deactivate()
  112. {
  113. if( !mActiveCount )
  114. return;
  115. mActiveCount --;
  116. if( !mActiveCount && !isDisabled() )
  117. _onDeactivate();
  118. }
  119. //-----------------------------------------------------------------------------
  120. void SFXState::enable()
  121. {
  122. if( !mDisableCount )
  123. return;
  124. mDisableCount --;
  125. if( !mDisableCount && mActiveCount > 0 )
  126. _onActivate();
  127. }
  128. //-----------------------------------------------------------------------------
  129. void SFXState::disable()
  130. {
  131. mDisableCount ++;
  132. if( mDisableCount == 1 && mActiveCount > 0 )
  133. _onDeactivate();
  134. }
  135. //-----------------------------------------------------------------------------
  136. bool SFXState::onAdd()
  137. {
  138. if( !Parent::onAdd() )
  139. return false;
  140. Sim::getSFXStateSet()->addObject( this );
  141. return true;
  142. }
  143. //-----------------------------------------------------------------------------
  144. bool SFXState::preload( bool server, String& errorStr )
  145. {
  146. if( !Parent::preload( server, errorStr ) )
  147. return false;
  148. if( !server )
  149. {
  150. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  151. if( !sfxResolve( &mIncludedStates[ i ], errorStr ) )
  152. return false;
  153. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  154. if( !sfxResolve( &mExcludedStates[ i ], errorStr ) )
  155. return false;
  156. }
  157. return true;
  158. }
  159. //-----------------------------------------------------------------------------
  160. void SFXState::packData( BitStream* stream )
  161. {
  162. Parent::packData( stream );
  163. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  164. sfxWrite( stream, mIncludedStates[ i ] );
  165. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  166. sfxWrite( stream, mExcludedStates[ i ] );
  167. }
  168. //-----------------------------------------------------------------------------
  169. void SFXState::unpackData( BitStream* stream )
  170. {
  171. Parent::unpackData( stream );
  172. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  173. sfxRead( stream, &mIncludedStates[ i ] );
  174. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  175. sfxRead( stream, &mExcludedStates[ i ] );
  176. }
  177. //-----------------------------------------------------------------------------
  178. void SFXState::_onActivate()
  179. {
  180. #ifdef DEBUG_SPEW
  181. Platform::outputDebugString( "[SFXState] Activating '%s'", getName() );
  182. #endif
  183. onActivate_callback();
  184. // Add the state to the list.
  185. sgActiveStates.push_back( this );
  186. // Activate included states.
  187. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  188. if( mIncludedStates[ i ] )
  189. mIncludedStates[ i ]->activate();
  190. // Disable excluded states.
  191. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  192. if( mExcludedStates[ i ] )
  193. mExcludedStates[ i ]->disable();
  194. }
  195. //-----------------------------------------------------------------------------
  196. void SFXState::_onDeactivate()
  197. {
  198. #ifdef DEBUG_SPEW
  199. Platform::outputDebugString( "[SFXState] Deactivating '%s'", getName() );
  200. #endif
  201. onDeactivate_callback();
  202. // Remove the state from the list.
  203. for( U32 i = 0; i < sgActiveStates.size(); ++ i )
  204. if( sgActiveStates[ i ] == this )
  205. {
  206. sgActiveStates.erase_fast( i );
  207. break;
  208. }
  209. // Deactivate included states.
  210. for( U32 i = 0; i < MaxIncludedStates; ++ i )
  211. if( mIncludedStates[ i ] )
  212. mIncludedStates[ i ]->deactivate();
  213. // Enable excluded states.
  214. for( U32 i = 0; i < MaxExcludedStates; ++ i )
  215. if( mExcludedStates[ i ] )
  216. mExcludedStates[ i ]->enable();
  217. }
  218. //=============================================================================
  219. // Console Methods.
  220. //=============================================================================
  221. // MARK: ---- Console Methods ----
  222. //-----------------------------------------------------------------------------
  223. DefineEngineMethod( SFXState, isActive, bool, (),,
  224. "Test whether the state is currently active.\n"
  225. "This is true when the activation count is >0 and the disabling count is =0.\n"
  226. "@return True if the state is currently active.\n"
  227. "@see activate" )
  228. {
  229. return object->isActive();
  230. }
  231. //-----------------------------------------------------------------------------
  232. DefineEngineMethod( SFXState, activate, void, (),,
  233. "Increase the activation count on the state.\n"
  234. "If the state isn't already active and it is not disabled, the state will be activated.\n"
  235. "@see isActive\n"
  236. "@see deactivate\n" )
  237. {
  238. object->activate();
  239. }
  240. //-----------------------------------------------------------------------------
  241. DefineEngineMethod( SFXState, deactivate, void, (),,
  242. "Decrease the activation count on the state.\n"
  243. "If the count reaches zero and the state was not disabled, the state will be deactivated.\n"
  244. "@see isActive\n"
  245. "@see activate\n" )
  246. {
  247. object->deactivate();
  248. }
  249. //-----------------------------------------------------------------------------
  250. DefineEngineMethod( SFXState, isDisabled, bool, (),,
  251. "Test whether the state is currently disabled.\n"
  252. "This is true when the disabling count of the state is non-zero.\n"
  253. "@return True if the state is disabled.\n\n"
  254. "@see disable\n" )
  255. {
  256. return object->isDisabled();
  257. }
  258. //-----------------------------------------------------------------------------
  259. DefineEngineMethod( SFXState, disable, void, (),,
  260. "Increase the disabling count of the state.\n"
  261. "If the state is currently active, it will be deactivated.\n"
  262. "@see isDisabled\n" )
  263. {
  264. object->disable();
  265. }
  266. //-----------------------------------------------------------------------------
  267. DefineEngineMethod( SFXState, enable, void, (),,
  268. "Decrease the disabling count of the state.\n"
  269. "If the disabling count reaches zero while the activation count is still non-zero, "
  270. "the state will be reactivated again.\n"
  271. "@see isDisabled\n" )
  272. {
  273. object->enable();
  274. }
  275. //=============================================================================
  276. // Console Functions.
  277. //=============================================================================
  278. // MARK: ---- Console Functions ----
  279. //-----------------------------------------------------------------------------
  280. DefineEngineFunction( sfxGetActiveStates, const char*, (),,
  281. "Return a newline-separated list of all active states.\n"
  282. "@return A list of the form\n"
  283. "@verbatim\n"
  284. "stateName1 NL stateName2 NL stateName3 ...\n"
  285. "@endverbatim\n"
  286. "where each element is the name of an active state object.\n\n"
  287. "@tsexample\n"
  288. "// Disable all active states.\n"
  289. "foreach$( %state in sfxGetActiveStates() )\n"
  290. " %state.disable();\n"
  291. "@endtsexample\n\n"
  292. "@ingroup SFX" )
  293. {
  294. StringBuilder str;
  295. bool isFirst = true;
  296. for( U32 i = 0; i < sgActiveStates.size(); ++ i )
  297. {
  298. if( !isFirst )
  299. str.append( ' ' );
  300. str.append( sgActiveStates[ i ]->getName() );
  301. isFirst = false;
  302. }
  303. return Con::getReturnBuffer( str );
  304. }