afxPhraseEffect.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  2. // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  3. // Copyright (C) 2015 Faust Logic, Inc.
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to
  7. // deal in the Software without restriction, including without limitation the
  8. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9. // sell copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. // IN THE SOFTWARE.
  22. //
  23. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  24. #include "afx/arcaneFX.h"
  25. #include "console/engineAPI.h"
  26. #include "afx/afxEffectWrapper.h"
  27. #include "afx/ce/afxPhraseEffect.h"
  28. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  29. // afxPhraseEffectData::ewValidator
  30. //
  31. // When an effect is added using "addEffect", this validator intercepts the value
  32. // and adds it to the dynamic effects list.
  33. //
  34. void afxPhraseEffectData::ewValidator::validateType(SimObject* object, void* typePtr)
  35. {
  36. afxPhraseEffectData* eff_data = dynamic_cast<afxPhraseEffectData*>(object);
  37. afxEffectBaseData** ew = (afxEffectBaseData**)(typePtr);
  38. if (eff_data && ew)
  39. {
  40. eff_data->fx_list.push_back(*ew);
  41. *ew = 0;
  42. }
  43. }
  44. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  45. // afxPhraseEffectData
  46. IMPLEMENT_CO_DATABLOCK_V1(afxPhraseEffectData);
  47. ConsoleDocClass( afxPhraseEffectData,
  48. "@brief A datablock that specifies a Phrase Effect, a grouping of other effects.\n\n"
  49. "A Phrase Effect is a grouping or phrase of effects that do nothing until certain trigger events occur. It's like having a whole "
  50. "Effectron organized as an individual effect."
  51. "\n\n"
  52. "Phrase effects can respond to a number of different kinds of triggers:\n"
  53. " -- Player triggers such as footsteps, jumps, landings, and idle triggers.\n"
  54. " -- Arbitrary animation triggers on dts-based scene objects.\n"
  55. " -- Arbitrary trigger bits assigned to active choreographer objects."
  56. "\n\n"
  57. "@ingroup afxEffects\n"
  58. "@ingroup AFX\n"
  59. "@ingroup Datablocks\n"
  60. );
  61. afxPhraseEffectData::afxPhraseEffectData()
  62. {
  63. duration = 0.0f;
  64. n_loops = 1;
  65. // dummy entry holds effect-wrapper pointer while a special validator
  66. // grabs it and adds it to an appropriate effects list
  67. dummy_fx_entry = NULL;
  68. // marked true if datablock ids need to
  69. // be converted into pointers
  70. do_id_convert = false;
  71. trigger_mask = 0;
  72. match_type = MATCH_ANY;
  73. match_state = STATE_ON;
  74. phrase_type = PHRASE_TRIGGERED;
  75. no_choreographer_trigs = false;
  76. no_cons_trigs = false;
  77. no_player_trigs = false;
  78. on_trig_cmd = ST_NULLSTRING;
  79. }
  80. afxPhraseEffectData::afxPhraseEffectData(const afxPhraseEffectData& other, bool temp_clone) : GameBaseData(other, temp_clone)
  81. {
  82. duration = other.duration;
  83. n_loops = other.n_loops;
  84. dummy_fx_entry = other.dummy_fx_entry;
  85. do_id_convert = other.do_id_convert; // --
  86. trigger_mask = other.trigger_mask;
  87. match_type = other.match_type;
  88. match_state = other.match_state;
  89. phrase_type = other.phrase_type;
  90. no_choreographer_trigs = other.no_choreographer_trigs;
  91. no_cons_trigs = other.no_cons_trigs;
  92. no_player_trigs = other.no_player_trigs;
  93. on_trig_cmd = other.on_trig_cmd;
  94. // fx_list; // -- ??
  95. }
  96. void afxPhraseEffectData::reloadReset()
  97. {
  98. fx_list.clear();
  99. }
  100. ImplementEnumType( afxPhraseEffect_MatchType, "Possible phrase effect match types.\n" "@ingroup afxPhraseEffect\n\n" )
  101. { afxPhraseEffectData::MATCH_ANY, "any", "..." },
  102. { afxPhraseEffectData::MATCH_ALL, "all", "..." },
  103. EndImplementEnumType;
  104. ImplementEnumType( afxPhraseEffect_StateType, "Possible phrase effect state types.\n" "@ingroup afxPhraseEffect\n\n" )
  105. { afxPhraseEffectData::STATE_ON, "on", "..." },
  106. { afxPhraseEffectData::STATE_OFF, "off", "..." },
  107. { afxPhraseEffectData::STATE_ON_AND_OFF, "both", "..." },
  108. EndImplementEnumType;
  109. ImplementEnumType( afxPhraseEffect_PhraseType, "Possible phrase effect types.\n" "@ingroup afxPhraseEffect\n\n" )
  110. { afxPhraseEffectData::PHRASE_TRIGGERED, "triggered", "..." },
  111. { afxPhraseEffectData::PHRASE_CONTINUOUS, "continuous", "..." },
  112. EndImplementEnumType;
  113. #define myOffset(field) Offset(field, afxPhraseEffectData)
  114. void afxPhraseEffectData::initPersistFields()
  115. {
  116. docsURL;
  117. addField("duration", TypeF32, myOffset(duration),
  118. "Specifies a duration for the phrase-effect. If set to infinity, the phrase-effect "
  119. "needs to have a phraseType of continuous. Set infinite duration using "
  120. "$AFX::INFINITE_TIME.");
  121. addField("numLoops", TypeS32, myOffset(n_loops),
  122. "Specifies the number of times the phrase-effect should loop. If set to infinity, "
  123. "the phrase-effect needs to have a phraseType of continuous. Set infinite looping "
  124. "using $AFX::INFINITE_REPEATS.");
  125. addField("triggerMask", TypeS32, myOffset(trigger_mask),
  126. "Sets which bits to consider in the current trigger-state which consists of 32 "
  127. "trigger-bits combined from (possibly overlapping) player trigger bits, constraint "
  128. "trigger bits, and choreographer trigger bits.");
  129. addField("matchType", TYPEID<afxPhraseEffectData::MatchType>(), myOffset(match_type),
  130. "Selects what combination of bits in triggerMask lead to a trigger. When set to "
  131. "'any', any bit in triggerMask matching the current trigger-state will cause a "
  132. "trigger. If set to 'all', every bit in triggerMask must match the trigger-state. "
  133. "Possible values: any or all.");
  134. addField("matchState", TYPEID<afxPhraseEffectData::StateType>(), myOffset(match_state),
  135. "Selects which bit-state(s) of bits in the triggerMask to consider when comparing to "
  136. "the current trigger-state. Possible values: on, off, or both.");
  137. addField("phraseType", TYPEID<afxPhraseEffectData::PhraseType>(), myOffset(phrase_type),
  138. "Selects between triggered and continuous types of phrases. When set to 'triggered', "
  139. "the phrase-effect is triggered when the relevant trigger-bits change state. When set "
  140. "to 'continuous', the phrase-effect will stay active as long as the trigger-bits "
  141. "remain in a matching state. Possible values: triggered or continuous.");
  142. addField("ignoreChoreographerTriggers", TypeBool, myOffset(no_choreographer_trigs),
  143. "When true, trigger-bits on the choreographer will be ignored.");
  144. addField("ignoreConstraintTriggers", TypeBool, myOffset(no_cons_trigs),
  145. "When true, animation triggers from dts-based constraint source objects will be "
  146. "ignored.");
  147. addField("ignorePlayerTriggers", TypeBool, myOffset(no_player_trigs),
  148. "When true, Player-specific triggers from Player-derived constraint source objects "
  149. "will be ignored.");
  150. addField("onTriggerCommand", TypeString, myOffset(on_trig_cmd),
  151. "Like a field substitution statement without the leading '$$' token, this eval "
  152. "statement will be executed when a trigger occurs. Any '%%' and '##' tokens will be "
  153. "substituted.");
  154. // effect lists
  155. // for each of these, dummy_fx_entry is set and then a validator adds it to the appropriate effects list
  156. static ewValidator emptyValidator(0);
  157. addFieldV("addEffect", TYPEID< afxEffectBaseData >(), myOffset(dummy_fx_entry), &emptyValidator,
  158. "A field macro which adds an effect wrapper datablock to a list of effects associated "
  159. "with the phrase-effect's single phrase. Unlike other fields, addEffect follows an "
  160. "unusual syntax. Order is important since the effects will resolve in the order they "
  161. "are added to each list.");
  162. Parent::initPersistFields();
  163. // disallow some field substitutions
  164. disableFieldSubstitutions("addEffect");
  165. }
  166. bool afxPhraseEffectData::onAdd()
  167. {
  168. if (Parent::onAdd() == false)
  169. return false;
  170. return true;
  171. }
  172. void afxPhraseEffectData::pack_fx(BitStream* stream, const afxEffectList& fx, bool packed)
  173. {
  174. stream->writeInt(fx.size(), EFFECTS_PER_PHRASE_BITS);
  175. for (int i = 0; i < fx.size(); i++)
  176. writeDatablockID(stream, fx[i], packed);
  177. }
  178. void afxPhraseEffectData::unpack_fx(BitStream* stream, afxEffectList& fx)
  179. {
  180. fx.clear();
  181. S32 n_fx = stream->readInt(EFFECTS_PER_PHRASE_BITS);
  182. for (int i = 0; i < n_fx; i++)
  183. fx.push_back((afxEffectWrapperData*)(uintptr_t)readDatablockID(stream));
  184. }
  185. void afxPhraseEffectData::packData(BitStream* stream)
  186. {
  187. Parent::packData(stream);
  188. stream->write(duration);
  189. stream->write(n_loops);
  190. stream->write(trigger_mask);
  191. stream->writeInt(match_type, 1);
  192. stream->writeInt(match_state, 2);
  193. stream->writeInt(phrase_type, 1);
  194. stream->writeFlag(no_choreographer_trigs);
  195. stream->writeFlag(no_cons_trigs);
  196. stream->writeFlag(no_player_trigs);
  197. stream->writeString(on_trig_cmd);
  198. pack_fx(stream, fx_list, mPacked);
  199. }
  200. void afxPhraseEffectData::unpackData(BitStream* stream)
  201. {
  202. Parent::unpackData(stream);
  203. stream->read(&duration);
  204. stream->read(&n_loops);
  205. stream->read(&trigger_mask);
  206. match_type = stream->readInt(1);
  207. match_state = stream->readInt(2);
  208. phrase_type = stream->readInt(1);
  209. no_choreographer_trigs = stream->readFlag();
  210. no_cons_trigs = stream->readFlag();
  211. no_player_trigs = stream->readFlag();
  212. on_trig_cmd = stream->readSTString();
  213. do_id_convert = true;
  214. unpack_fx(stream, fx_list);
  215. }
  216. bool afxPhraseEffectData::preload(bool server, String &errorStr)
  217. {
  218. if (!Parent::preload(server, errorStr))
  219. return false;
  220. // Resolve objects transmitted from server
  221. if (!server)
  222. {
  223. if (do_id_convert)
  224. {
  225. for (S32 i = 0; i < fx_list.size(); i++)
  226. {
  227. SimObjectId db_id = SimObjectId((uintptr_t)fx_list[i]);
  228. if (db_id != 0)
  229. {
  230. // try to convert id to pointer
  231. if (!Sim::findObject(db_id, fx_list[i]))
  232. {
  233. Con::errorf(ConsoleLogEntry::General,
  234. "afxPhraseEffectData::preload() -- bad datablockId: 0x%x",
  235. db_id);
  236. }
  237. }
  238. }
  239. do_id_convert = false;
  240. }
  241. }
  242. return true;
  243. }
  244. void afxPhraseEffectData::gather_cons_defs(Vector<afxConstraintDef>& defs)
  245. {
  246. afxConstraintDef::gather_cons_defs(defs, fx_list);
  247. }
  248. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
  249. DefineEngineMethod( afxPhraseEffectData, pushEffect, void, ( afxEffectBaseData* effectData ),,
  250. "Add a child effect to a phrase effect datablock. Argument can be an afxEffectWrappperData or an afxEffectGroupData.\n" )
  251. {
  252. if (!effectData)
  253. {
  254. Con::errorf("afxPhraseEffectData::pushEffect() -- failed to resolve effect datablock.");
  255. return;
  256. }
  257. object->fx_list.push_back(effectData);
  258. }
  259. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//