afxPhraseEffect.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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. addField("duration", TypeF32, myOffset(duration),
  117. "Specifies a duration for the phrase-effect. If set to infinity, the phrase-effect "
  118. "needs to have a phraseType of continuous. Set infinite duration using "
  119. "$AFX::INFINITE_TIME.");
  120. addField("numLoops", TypeS32, myOffset(n_loops),
  121. "Specifies the number of times the phrase-effect should loop. If set to infinity, "
  122. "the phrase-effect needs to have a phraseType of continuous. Set infinite looping "
  123. "using $AFX::INFINITE_REPEATS.");
  124. addField("triggerMask", TypeS32, myOffset(trigger_mask),
  125. "Sets which bits to consider in the current trigger-state which consists of 32 "
  126. "trigger-bits combined from (possibly overlapping) player trigger bits, constraint "
  127. "trigger bits, and choreographer trigger bits.");
  128. addField("matchType", TYPEID<afxPhraseEffectData::MatchType>(), myOffset(match_type),
  129. "Selects what combination of bits in triggerMask lead to a trigger. When set to "
  130. "'any', any bit in triggerMask matching the current trigger-state will cause a "
  131. "trigger. If set to 'all', every bit in triggerMask must match the trigger-state. "
  132. "Possible values: any or all.");
  133. addField("matchState", TYPEID<afxPhraseEffectData::StateType>(), myOffset(match_state),
  134. "Selects which bit-state(s) of bits in the triggerMask to consider when comparing to "
  135. "the current trigger-state. Possible values: on, off, or both.");
  136. addField("phraseType", TYPEID<afxPhraseEffectData::PhraseType>(), myOffset(phrase_type),
  137. "Selects between triggered and continuous types of phrases. When set to 'triggered', "
  138. "the phrase-effect is triggered when the relevant trigger-bits change state. When set "
  139. "to 'continuous', the phrase-effect will stay active as long as the trigger-bits "
  140. "remain in a matching state. Possible values: triggered or continuous.");
  141. addField("ignoreChoreographerTriggers", TypeBool, myOffset(no_choreographer_trigs),
  142. "When true, trigger-bits on the choreographer will be ignored.");
  143. addField("ignoreConstraintTriggers", TypeBool, myOffset(no_cons_trigs),
  144. "When true, animation triggers from dts-based constraint source objects will be "
  145. "ignored.");
  146. addField("ignorePlayerTriggers", TypeBool, myOffset(no_player_trigs),
  147. "When true, Player-specific triggers from Player-derived constraint source objects "
  148. "will be ignored.");
  149. addField("onTriggerCommand", TypeString, myOffset(on_trig_cmd),
  150. "Like a field substitution statement without the leading '$$' token, this eval "
  151. "statement will be executed when a trigger occurs. Any '%%' and '##' tokens will be "
  152. "substituted.");
  153. // effect lists
  154. // for each of these, dummy_fx_entry is set and then a validator adds it to the appropriate effects list
  155. static ewValidator emptyValidator(0);
  156. addFieldV("addEffect", TYPEID< afxEffectBaseData >(), myOffset(dummy_fx_entry), &emptyValidator,
  157. "A field macro which adds an effect wrapper datablock to a list of effects associated "
  158. "with the phrase-effect's single phrase. Unlike other fields, addEffect follows an "
  159. "unusual syntax. Order is important since the effects will resolve in the order they "
  160. "are added to each list.");
  161. Parent::initPersistFields();
  162. // disallow some field substitutions
  163. disableFieldSubstitutions("addEffect");
  164. }
  165. bool afxPhraseEffectData::onAdd()
  166. {
  167. if (Parent::onAdd() == false)
  168. return false;
  169. return true;
  170. }
  171. void afxPhraseEffectData::pack_fx(BitStream* stream, const afxEffectList& fx, bool packed)
  172. {
  173. stream->writeInt(fx.size(), EFFECTS_PER_PHRASE_BITS);
  174. for (int i = 0; i < fx.size(); i++)
  175. writeDatablockID(stream, fx[i], packed);
  176. }
  177. void afxPhraseEffectData::unpack_fx(BitStream* stream, afxEffectList& fx)
  178. {
  179. fx.clear();
  180. S32 n_fx = stream->readInt(EFFECTS_PER_PHRASE_BITS);
  181. for (int i = 0; i < n_fx; i++)
  182. fx.push_back((afxEffectWrapperData*)(uintptr_t)readDatablockID(stream));
  183. }
  184. void afxPhraseEffectData::packData(BitStream* stream)
  185. {
  186. Parent::packData(stream);
  187. stream->write(duration);
  188. stream->write(n_loops);
  189. stream->write(trigger_mask);
  190. stream->writeInt(match_type, 1);
  191. stream->writeInt(match_state, 2);
  192. stream->writeInt(phrase_type, 1);
  193. stream->writeFlag(no_choreographer_trigs);
  194. stream->writeFlag(no_cons_trigs);
  195. stream->writeFlag(no_player_trigs);
  196. stream->writeString(on_trig_cmd);
  197. pack_fx(stream, fx_list, mPacked);
  198. }
  199. void afxPhraseEffectData::unpackData(BitStream* stream)
  200. {
  201. Parent::unpackData(stream);
  202. stream->read(&duration);
  203. stream->read(&n_loops);
  204. stream->read(&trigger_mask);
  205. match_type = stream->readInt(1);
  206. match_state = stream->readInt(2);
  207. phrase_type = stream->readInt(1);
  208. no_choreographer_trigs = stream->readFlag();
  209. no_cons_trigs = stream->readFlag();
  210. no_player_trigs = stream->readFlag();
  211. on_trig_cmd = stream->readSTString();
  212. do_id_convert = true;
  213. unpack_fx(stream, fx_list);
  214. }
  215. bool afxPhraseEffectData::preload(bool server, String &errorStr)
  216. {
  217. if (!Parent::preload(server, errorStr))
  218. return false;
  219. // Resolve objects transmitted from server
  220. if (!server)
  221. {
  222. if (do_id_convert)
  223. {
  224. for (S32 i = 0; i < fx_list.size(); i++)
  225. {
  226. SimObjectId db_id = SimObjectId((uintptr_t)fx_list[i]);
  227. if (db_id != 0)
  228. {
  229. // try to convert id to pointer
  230. if (!Sim::findObject(db_id, fx_list[i]))
  231. {
  232. Con::errorf(ConsoleLogEntry::General,
  233. "afxPhraseEffectData::preload() -- bad datablockId: 0x%x",
  234. db_id);
  235. }
  236. }
  237. }
  238. do_id_convert = false;
  239. }
  240. }
  241. return true;
  242. }
  243. void afxPhraseEffectData::gather_cons_defs(Vector<afxConstraintDef>& defs)
  244. {
  245. afxConstraintDef::gather_cons_defs(defs, fx_list);
  246. }
  247. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
  248. DefineEngineMethod( afxPhraseEffectData, pushEffect, void, ( afxEffectBaseData* effectData ),,
  249. "Add a child effect to a phrase effect datablock. Argument can be an afxEffectWrappperData or an afxEffectGroupData.\n" )
  250. {
  251. if (!effectData)
  252. {
  253. Con::errorf("afxPhraseEffectData::pushEffect() -- failed to resolve effect datablock.");
  254. return;
  255. }
  256. object->fx_list.push_back(effectData);
  257. }
  258. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//