afxEA_PhraseEffect.cpp
Engine/source/afx/ea/afxEA_PhraseEffect.cpp
Classes:
class
Detailed Description
1 2 3//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 4// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames 5// Copyright (C) 2015 Faust Logic, Inc. 6// 7// Permission is hereby granted, free of charge, to any person obtaining a copy 8// of this software and associated documentation files (the "Software"), to 9// deal in the Software without restriction, including without limitation the 10// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11// sell copies of the Software, and to permit persons to whom the Software is 12// furnished to do so, subject to the following conditions: 13// 14// The above copyright notice and this permission notice shall be included in 15// all copies or substantial portions of the Software. 16// 17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23// IN THE SOFTWARE. 24// 25//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 26 27#include <typeinfo> 28#include "afx/arcaneFX.h" 29 30#include "console/compiler.h" 31#include "T3D/player.h" 32 33#include "afx/afxEffectDefs.h" 34#include "afx/afxPhrase.h" 35#include "afx/afxEffectWrapper.h" 36#include "afx/afxChoreographer.h" 37#include "afx/ce/afxPhraseEffect.h" 38 39//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 40// afxEA_PhraseEffect 41 42class afxEA_PhraseEffect : public afxEffectWrapper 43{ 44 typedef afxEffectWrapper Parent; 45 46 afxPhraseEffectData* phrase_fx_data; 47 Vector<afxPhrase*>* active_phrases; 48 U32 last_trigger_mask; 49 50 Vector<afxPhrase*> _phrases_a; 51 Vector<afxPhrase*> _phrases_b; 52 53 void grab_constraint_triggers(U32& trigger_mask); 54 void grab_player_triggers(U32& trigger_mask); 55 56 void do_runtime_substitutions(); 57 void trigger_new_phrase(); 58 void update_active_phrases(F32 dt); 59 void cleanup_finished_phrases(); 60 61public: 62 /*C*/ afxEA_PhraseEffect(); 63 /*D*/ ~afxEA_PhraseEffect(); 64 65 virtual void ea_set_datablock(SimDataBlock*); 66 virtual bool ea_start(); 67 virtual bool ea_update(F32 dt); 68 virtual void ea_finish(bool was_stopped); 69 70 virtual bool ea_is_enabled() { return true; } 71}; 72 73//~~~~~~~~~~~~~~~~~~~~// 74 75afxEA_PhraseEffect::afxEA_PhraseEffect() 76{ 77 phrase_fx_data = 0; 78 active_phrases = &_phrases_a; 79 last_trigger_mask = 0; 80} 81 82afxEA_PhraseEffect::~afxEA_PhraseEffect() 83{ 84 if (phrase_fx_data && phrase_fx_data->isTempClone()) 85 delete phrase_fx_data; 86 phrase_fx_data = 0; 87 88 for (S32 i = 0; i < _phrases_a.size(); i++) 89 { 90 if (_phrases_a[i]) 91 delete _phrases_a[i]; 92 } 93 94 for (S32 i = 0; i < _phrases_b.size(); i++) 95 { 96 if (_phrases_b[i]) 97 delete _phrases_b[i]; 98 } 99} 100 101void afxEA_PhraseEffect::ea_set_datablock(SimDataBlock* db) 102{ 103 phrase_fx_data = dynamic_cast<afxPhraseEffectData*>(db); 104} 105 106bool afxEA_PhraseEffect::ea_start() 107{ 108 if (!phrase_fx_data) 109 { 110 Con::errorf("afxEA_PhraseEffect::ea_start() -- missing or incompatible datablock."); 111 return false; 112 } 113 114 //last_trigger_mask = choreographer->getTriggerMask(); 115 last_trigger_mask = 0xffffffff; 116 117 return true; 118} 119 120void afxEA_PhraseEffect::grab_constraint_triggers(U32& trigger_mask) 121{ 122 afxConstraint* pos_cons = getPosConstraint(); 123 if (pos_cons) 124 trigger_mask |= pos_cons->getTriggers(); 125} 126 127void afxEA_PhraseEffect::grab_player_triggers(U32& trigger_mask) 128{ 129 afxConstraint* pos_cons = getPosConstraint(); 130 Player* player = (pos_cons) ? dynamic_cast<Player*>(pos_cons->getSceneObject()) : 0; 131 if (player) 132 { 133 if (player->isClientObject()) 134 trigger_mask |= player->getClientEventTriggers(); 135 else 136 trigger_mask |= player->getServerEventTriggers(); 137 } 138} 139 140bool afxEA_PhraseEffect::ea_update(F32 dt) 141{ 142 if (mFade_value >= 1.0f) 143 { 144 // 145 // Choreographer Triggers: 146 // These triggers can come from the choreographer owning this effect. 147 // They must be set explicitly by calls to afxChoreographer 148 // console-methods, setTriggerBit(), or clearTriggerBit(). 149 // 150 U32 trigger_mask = (phrase_fx_data->no_choreographer_trigs) ? 0 : mChoreographer->getTriggerMask(); 151 152 // 153 // Constraint Triggers: 154 // These triggers can come from the position contraint if it is: 155 // -- a TSStatic or ShapeBase derived object with dts triggers. 156 // -- a trigger producing effect such as afxModel. 157 // 158 if (!phrase_fx_data->no_cons_trigs) 159 grab_constraint_triggers(trigger_mask); 160 161 // 162 // Player Triggers: 163 // These triggers can come from the position contraint if it is 164 // a Player or Player-derived object. 165 // 166 if (!phrase_fx_data->no_player_trigs) 167 grab_player_triggers(trigger_mask); 168 169 // any change in the triggers? 170 if (trigger_mask != last_trigger_mask) 171 { 172 if (phrase_fx_data->phrase_type == afxPhraseEffectData::PHRASE_CONTINUOUS) 173 { 174 bool last_state, new_state; 175 if (phrase_fx_data->match_type == afxPhraseEffectData::MATCH_ANY) 176 { 177 last_state = ((last_trigger_mask & phrase_fx_data->trigger_mask) != 0); 178 new_state = ((trigger_mask & phrase_fx_data->trigger_mask) != 0); 179 } 180 else 181 { 182 last_state = ((last_trigger_mask & phrase_fx_data->trigger_mask) == phrase_fx_data->trigger_mask); 183 new_state = ((trigger_mask & phrase_fx_data->trigger_mask) == phrase_fx_data->trigger_mask); 184 } 185 if (new_state != last_state) 186 { 187 bool state_on = phrase_fx_data->match_state & afxPhraseEffectData::STATE_ON; 188 if (new_state == state_on) // start trigger 189 { 190 trigger_new_phrase(); 191 } 192 else // stop trigger 193 { 194 for (S32 i = 0; i < active_phrases->size(); i++) 195 { 196 (*active_phrases)[i]->stop(mLife_elapsed); 197 } 198 } 199 } 200 } 201 else // if (phrase_fx_data->phrase_type == afxPhraseEffectData::PHRASE_TRIGGERED) 202 { 203 bool did_trigger = false; 204 U32 changed_bits = (last_trigger_mask ^ trigger_mask); 205 206 if ((phrase_fx_data->match_state & afxPhraseEffectData::STATE_ON) != 0) 207 { 208 // check for trigger bits that just switched to on state 209 U32 changed_on_bits = (changed_bits & trigger_mask); 210 if (phrase_fx_data->match_type == afxPhraseEffectData::MATCH_ANY) 211 did_trigger = ((changed_on_bits & phrase_fx_data->trigger_mask) != 0); 212 else 213 did_trigger = ((changed_on_bits & phrase_fx_data->trigger_mask) == phrase_fx_data->trigger_mask); 214 } 215 216 if (!did_trigger && ((phrase_fx_data->match_state & afxPhraseEffectData::STATE_OFF) != 0)) 217 { 218 // check for trigger bits that just switched to off state 219 U32 changed_off_bits = (changed_bits & last_trigger_mask); 220 if (phrase_fx_data->match_type == afxPhraseEffectData::MATCH_ANY) 221 did_trigger = ((changed_off_bits & phrase_fx_data->trigger_mask) != 0); 222 else 223 did_trigger = ((changed_off_bits & phrase_fx_data->trigger_mask) == phrase_fx_data->trigger_mask); 224 } 225 226 if (did_trigger) 227 trigger_new_phrase(); 228 } 229 230 last_trigger_mask = trigger_mask; 231 } 232 } 233 234 update_active_phrases(dt); 235 236 cleanup_finished_phrases(); 237 238 return true; 239} 240 241void afxEA_PhraseEffect::ea_finish(bool was_stopped) 242{ 243 for (S32 i = 0; i < active_phrases->size(); i++) 244 { 245 (*active_phrases)[i]->stop(mLife_elapsed); 246 } 247} 248 249void afxEA_PhraseEffect::do_runtime_substitutions() 250{ 251 // only clone the datablock if there are substitutions 252 if (phrase_fx_data->getSubstitutionCount() > 0) 253 { 254 // clone the datablock and perform substitutions 255 afxPhraseEffectData* orig_db = phrase_fx_data; 256 phrase_fx_data = new afxPhraseEffectData(*orig_db, true); 257 orig_db->performSubstitutions(phrase_fx_data, mChoreographer, mGroup_index); 258 } 259} 260 261void afxEA_PhraseEffect::trigger_new_phrase() 262{ 263 //afxPhrase* phrase = new afxPhrase(choreographer->isServerObject(), /*willStop=*/false); 264 bool will_stop = phrase_fx_data->phrase_type == afxPhraseEffectData::PHRASE_CONTINUOUS; 265 afxPhrase* phrase = new afxPhrase(mChoreographer->isServerObject(), will_stop); 266 phrase->init(phrase_fx_data->fx_list, mDatablock->ewd_timing.lifetime, mChoreographer, mTime_factor, phrase_fx_data->n_loops, mGroup_index); 267 phrase->start(0, 0); 268 if (phrase->isEmpty()) 269 { 270 delete phrase; 271 return; 272 } 273 274 if (phrase_fx_data->on_trig_cmd != ST_NULLSTRING) 275 { 276 char obj_str[32]; 277 dStrcpy(obj_str, Con::getIntArg(mChoreographer->getId()), 32); 278 279 char index_str[32]; 280 dStrcpy(index_str, Con::getIntArg(mGroup_index), 32); 281 282 char buffer[1024]; 283 char* b = buffer; 284 const char* v = phrase_fx_data->on_trig_cmd; 285 while (*v != '\0') 286 { 287 if (v[0] == '%' && v[1] == '%') 288 { 289 const char* s = obj_str; 290 while (*s != '\0') 291 { 292 b[0] = s[0]; 293 b++; 294 s++; 295 } 296 v += 2; 297 } 298 else if (v[0] == '#' && v[1] == '#') 299 { 300 const char* s = index_str; 301 while (*s != '\0') 302 { 303 b[0] = s[0]; 304 b++; 305 s++; 306 } 307 v += 2; 308 } 309 else 310 { 311 b[0] = v[0]; 312 b++; 313 v++; 314 } 315 } 316 b[0] = '\0'; 317 318 Compiler::gSyntaxError = false; 319 //Con::errorf("EVAL [%s]", avar("%s;", buffer)); 320 Con::evaluate(avar("%s;", buffer), false, 0); 321 if (Compiler::gSyntaxError) 322 { 323 Con::errorf("onTriggerCommand \"%s\" -- syntax error", phrase_fx_data->on_trig_cmd); 324 Compiler::gSyntaxError = false; 325 } 326 } 327 328 active_phrases->push_back(phrase); 329} 330 331void afxEA_PhraseEffect::update_active_phrases(F32 dt) 332{ 333 for (S32 i = 0; i < active_phrases->size(); i++) 334 { 335 afxPhrase* phrase = (*active_phrases)[i]; 336 if (phrase->expired(mLife_elapsed)) 337 phrase->recycle(mLife_elapsed); 338 phrase->update(dt, mLife_elapsed); 339 } 340} 341 342void afxEA_PhraseEffect::cleanup_finished_phrases() 343{ 344 Vector<afxPhrase*>* surviving_phrases = (active_phrases == &_phrases_a) ? &_phrases_b : &_phrases_a; 345 346 surviving_phrases->clear(); 347 for (S32 i = 0; i < active_phrases->size(); i++) 348 { 349 afxPhrase* phrase = (*active_phrases)[i]; 350 if (!phrase->isEmpty()) 351 surviving_phrases->push_back(phrase); 352 else 353 delete phrase; 354 } 355 356 active_phrases->clear(); 357 active_phrases = surviving_phrases; 358} 359 360//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 361 362class afxEA_PhraseEffectDesc : public afxEffectAdapterDesc, public afxEffectDefs 363{ 364 static afxEA_PhraseEffectDesc desc; 365 366public: 367 virtual bool testEffectType(const SimDataBlock*) const; 368 virtual bool requiresStop(const afxEffectWrapperData*, const afxEffectTimingData&) const; 369 virtual bool runsOnServer(const afxEffectWrapperData*) const { return true; } 370 virtual bool runsOnClient(const afxEffectWrapperData*) const { return true; } 371 372 virtual afxEffectWrapper* create() const { return new afxEA_PhraseEffect; } 373}; 374 375afxEA_PhraseEffectDesc afxEA_PhraseEffectDesc::desc; 376 377bool afxEA_PhraseEffectDesc::testEffectType(const SimDataBlock* db) const 378{ 379 return (typeid(afxPhraseEffectData) == typeid(*db)); 380} 381 382bool afxEA_PhraseEffectDesc::requiresStop(const afxEffectWrapperData* ew, const afxEffectTimingData& timing) const 383{ 384 return (timing.lifetime < 0); 385} 386 387//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 388