afxEffectron.cpp
Engine/source/afx/afxEffectron.cpp
Classes:
Public Defines
define
myOffset(field) (field, )
Public Functions
ConsoleDocClass(afxEffectron , "@brief A basic effects <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">choreographer.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxChoreographers\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
ConsoleDocClass(afxEffectronData , "@brief Defines the properties of an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffectron.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxChoreographers\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
DefineEngineFunction(startEffectron , S32 , (afxEffectronData *datablock, const char *constraintSource, const char *constraintName, SimObject *extra) , (nullAsType< afxEffectronData * >(), nullAsType< const char * >(), nullAsType< const char * >(), nullAsType< SimObject * >()) , "Instantiates the effectron defined by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablock.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxEffectron , activate , void , () )
DefineEngineMethod(afxEffectron , interrupt , void , () , "Interrupts and deletes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> running <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effectron.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxEffectron , setTimeFactor , void , (float factor) , "Sets the time-factor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effectron.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxEffectronData , pushEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> an effectron's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxEffectronData , reset , void , () , "Resets an effectron datablock during <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">reload.\n\n</a>" "@ingroup AFX" )
Detailed Description
Public Defines
myOffset(field) (field, )
Public Functions
ConsoleDocClass(afxEffectron , "@brief A basic effects <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">choreographer.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxChoreographers\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
ConsoleDocClass(afxEffectronData , "@brief Defines the properties of an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffectron.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxChoreographers\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
DefineEngineFunction(startEffectron , S32 , (afxEffectronData *datablock, const char *constraintSource, const char *constraintName, SimObject *extra) , (nullAsType< afxEffectronData * >(), nullAsType< const char * >(), nullAsType< const char * >(), nullAsType< SimObject * >()) , "Instantiates the effectron defined by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">datablock.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxEffectron , activate , void , () )
DefineEngineMethod(afxEffectron , interrupt , void , () , "Interrupts and deletes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> running <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effectron.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxEffectron , setTimeFactor , void , (float factor) , "Sets the time-factor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effectron.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxEffectronData , pushEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> an effectron's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxEffectronData , reset , void , () , "Resets an effectron datablock during <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">reload.\n\n</a>" "@ingroup AFX" )
IMPLEMENT_CO_DATABLOCK_V1(afxEffectronData )
IMPLEMENT_CO_NETOBJECT_V1(afxEffectron )
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 "afx/arcaneFX.h" 28 29#include "console/engineAPI.h" 30#include "T3D/gameBase/gameConnection.h" 31#include "sfx/sfxSystem.h" 32#include "math/mathIO.h" 33 34#include "afx/afxChoreographer.h" 35#include "afx/afxEffectron.h" 36 37//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 38// afxEffectronData::ewValidator 39// 40// When an effect is added using "addEffect", this validator intercepts the value 41// and adds it to the dynamic effects list. 42// 43void afxEffectronData::ewValidator::validateType(SimObject* object, void* typePtr) 44{ 45 afxEffectronData* eff_data = dynamic_cast<afxEffectronData*>(object); 46 afxEffectBaseData** ew = (afxEffectBaseData**)(typePtr); 47 48 if (eff_data && ew) 49 { 50 eff_data->fx_list.push_back(*ew); 51 *ew = 0; 52 } 53} 54 55//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 56 57class EffectronFinishStartupEvent : public SimEvent 58{ 59public: 60 void process(SimObject* obj) 61 { 62 afxEffectron* eff = dynamic_cast<afxEffectron*>(obj); 63 if (eff) 64 eff->finish_startup(); 65 } 66}; 67 68//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 69//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 70// afxEffectronData 71 72IMPLEMENT_CO_DATABLOCK_V1(afxEffectronData); 73 74ConsoleDocClass( afxEffectronData, 75 "@brief Defines the properties of an afxEffectron.\n\n" 76 77 "@ingroup afxChoreographers\n" 78 "@ingroup AFX\n" 79 "@ingroup Datablocks\n" 80); 81 82afxEffectronData::afxEffectronData() 83{ 84 duration = 0.0f; 85 n_loops = 1; 86 87 // dummy entry holds effect-wrapper pointer while a special validator 88 // grabs it and adds it to an appropriate effects list 89 dummy_fx_entry = NULL; 90 91 // marked true if datablock ids need to 92 // be converted into pointers 93 do_id_convert = false; 94} 95 96afxEffectronData::afxEffectronData(const afxEffectronData& other, bool temp_clone) : afxChoreographerData(other, temp_clone) 97{ 98 duration = other.duration; 99 n_loops = other.n_loops; 100 dummy_fx_entry = other.dummy_fx_entry; 101 do_id_convert = other.do_id_convert; 102 fx_list = other.fx_list; 103} 104 105void afxEffectronData::reloadReset() 106{ 107 fx_list.clear(); 108} 109 110#define myOffset(field) Offset(field, afxEffectronData) 111 112void afxEffectronData::initPersistFields() 113{ 114 addField("duration", TypeF32, myOffset(duration), 115 "..."); 116 addField("numLoops", TypeS32, myOffset(n_loops), 117 "..."); 118 // effect lists 119 // for each of these, dummy_fx_entry is set and then a validator adds it to the appropriate effects list 120 static ewValidator emptyValidator(0); 121 122 addFieldV("addEffect", TYPEID<afxEffectBaseData>(), myOffset(dummy_fx_entry), &emptyValidator, 123 "..."); 124 125 Parent::initPersistFields(); 126 127 // disallow some field substitutions 128 disableFieldSubstitutions("addEffect"); 129} 130 131bool afxEffectronData::onAdd() 132{ 133 if (Parent::onAdd() == false) 134 return false; 135 136 return true; 137} 138 139void afxEffectronData::pack_fx(BitStream* stream, const afxEffectList& fx, bool packed) 140{ 141 stream->writeInt(fx.size(), EFFECTS_PER_PHRASE_BITS); 142 for (int i = 0; i < fx.size(); i++) 143 writeDatablockID(stream, fx[i], packed); 144} 145 146void afxEffectronData::unpack_fx(BitStream* stream, afxEffectList& fx) 147{ 148 fx.clear(); 149 S32 n_fx = stream->readInt(EFFECTS_PER_PHRASE_BITS); 150 for (int i = 0; i < n_fx; i++) 151 fx.push_back((afxEffectWrapperData*)(uintptr_t)readDatablockID(stream)); 152} 153 154void afxEffectronData::packData(BitStream* stream) 155{ 156 Parent::packData(stream); 157 158 stream->write(duration); 159 stream->write(n_loops); 160 161 pack_fx(stream, fx_list, mPacked); 162} 163 164void afxEffectronData::unpackData(BitStream* stream) 165{ 166 Parent::unpackData(stream); 167 168 stream->read(&duration); 169 stream->read(&n_loops); 170 171 do_id_convert = true; 172 unpack_fx(stream, fx_list); 173} 174 175bool afxEffectronData::preload(bool server, String &errorStr) 176{ 177 if (!Parent::preload(server, errorStr)) 178 return false; 179 180 // Resolve objects transmitted from server 181 if (!server) 182 { 183 if (do_id_convert) 184 { 185 for (S32 i = 0; i < fx_list.size(); i++) 186 { 187 SimObjectId db_id = SimObjectId((uintptr_t)fx_list[i]); 188 if (db_id != 0) 189 { 190 // try to convert id to pointer 191 if (!Sim::findObject(db_id, fx_list[i])) 192 { 193 Con::errorf(ConsoleLogEntry::General, 194 "afxEffectronData::preload() -- bad datablockId: 0x%x", 195 db_id); 196 } 197 } 198 } 199 do_id_convert = false; 200 } 201 } 202 203 return true; 204} 205 206void afxEffectronData::gatherConstraintDefs(Vector<afxConstraintDef>& defs) 207{ 208 afxConstraintDef::gather_cons_defs(defs, fx_list); 209} 210 211//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~// 212 213DefineEngineMethod(afxEffectronData, reset, void, (),, 214 "Resets an effectron datablock during reload.\n\n" 215 "@ingroup AFX") 216{ 217 object->reloadReset(); 218} 219 220DefineEngineMethod(afxEffectronData, pushEffect, void, (afxEffectBaseData* effect),, 221 "Adds an effect (wrapper or group) to an effectron's phase.\n\n" 222 "@ingroup AFX") 223{ 224 if (!effect) 225 { 226 Con::errorf("afxEffectronData::pushEffect() -- missing afxEffectWrapperData."); 227 return; 228 } 229 230 object->fx_list.push_back(effect); 231} 232 233//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 234//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 235// afxEffectron 236 237IMPLEMENT_CO_NETOBJECT_V1(afxEffectron); 238 239ConsoleDocClass( afxEffectron, 240 "@brief A basic effects choreographer.\n\n" 241 242 "@ingroup afxChoreographers\n" 243 "@ingroup AFX\n" 244); 245 246StringTableEntry afxEffectron::CAMERA_CONS; 247StringTableEntry afxEffectron::LISTENER_CONS; 248 249void afxEffectron::init() 250{ 251 // setup static predefined constraint names 252 if (CAMERA_CONS == 0) 253 { 254 CAMERA_CONS = StringTable->insert("camera"); 255 LISTENER_CONS = StringTable->insert("listener"); 256 } 257 258 // afxEffectron is always in scope, however the effects used 259 // do their own scoping in that they will shut off if their 260 // position constraint leaves scope. 261 // 262 // note -- ghosting is delayed until constraint 263 // initialization is done. 264 // 265 //mNetFlags.set(Ghostable | ScopeAlways); 266 mNetFlags.clear(Ghostable | ScopeAlways); 267 268 datablock = NULL; 269 exeblock = NULL; 270 271 constraints_initialized = false; 272 scoping_initialized = false; 273 274 effect_state = (U8) INACTIVE_STATE; 275 effect_elapsed = 0; 276 277 // define named constraints 278 constraint_mgr->defineConstraint(CAMERA_CONSTRAINT, CAMERA_CONS); 279 constraint_mgr->defineConstraint(POINT_CONSTRAINT, LISTENER_CONS); 280 281 active_phrase = NULL; 282 time_factor = 1.0f; 283 camera_cons_obj = 0; 284 285 marks_mask = 0; 286} 287 288afxEffectron::afxEffectron() 289{ 290 started_with_newop = true; 291 init(); 292} 293 294afxEffectron::afxEffectron(bool not_default) 295{ 296 started_with_newop = false; 297 init(); 298} 299 300afxEffectron::~afxEffectron() 301{ 302 delete active_phrase; 303 304 if (datablock && datablock->isTempClone()) 305 { 306 delete datablock; 307 datablock = 0; 308 } 309} 310 311//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 312 313// STANDARD OVERLOADED METHODS // 314 315bool afxEffectron::onNewDataBlock(GameBaseData* dptr, bool reload) 316{ 317 datablock = dynamic_cast<afxEffectronData*>(dptr); 318 if (!datablock || !Parent::onNewDataBlock(dptr, reload)) 319 return false; 320 321 if (isServerObject() && started_with_newop) 322 { 323 // copy dynamic fields from the datablock but 324 // don't replace fields with a value 325 assignDynamicFieldsFrom(dptr, arcaneFX::sParameterFieldPrefix, true); 326 } 327 328 exeblock = datablock; 329 330 if (isClientObject()) 331 { 332 // make a temp datablock clone if there are substitutions 333 if (datablock->getSubstitutionCount() > 0) 334 { 335 afxEffectronData* orig_db = datablock; 336 datablock = new afxEffectronData(*orig_db, true); 337 exeblock = orig_db; 338 // Don't perform substitutions yet, the effectrons's dynamic fields haven't 339 // arrived yet and the substitutions may refer to them. Hold off and do 340 // in in the onAdd() method. 341 } 342 } 343 else if (started_with_newop) 344 { 345 // make a temp datablock clone if there are substitutions 346 if (datablock->getSubstitutionCount() > 0) 347 { 348 afxEffectronData* orig_db = datablock; 349 datablock = new afxEffectronData(*orig_db, true); 350 exeblock = orig_db; 351 orig_db->performSubstitutions(datablock, this, ranking); 352 } 353 } 354 355 return true; 356} 357 358void afxEffectron::processTick(const Move* m) 359{ 360 Parent::processTick(m); 361 362 // don't process moves or client ticks 363 if (m != 0 || isClientObject()) 364 return; 365 366 process_server(); 367} 368 369void afxEffectron::advanceTime(F32 dt) 370{ 371 Parent::advanceTime(dt); 372 373 process_client(dt); 374} 375 376bool afxEffectron::onAdd() 377{ 378 if (!Parent::onAdd()) 379 return false; 380 381 if (isClientObject()) 382 { 383 if (datablock->isTempClone()) 384 { 385 afxEffectronData* orig_db = (afxEffectronData*)exeblock; 386 orig_db->performSubstitutions(datablock, this, ranking); 387 } 388 } 389 else if (started_with_newop && !postpone_activation) 390 { 391 if (!activationCallInit()) 392 return false; 393 activate(); 394 } 395 396 return true; 397} 398 399//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 400 401U32 afxEffectron::packUpdate(NetConnection* conn, U32 mask, BitStream* stream) 402{ 403 S32 mark_stream_pos = stream->getCurPos(); 404 405 U32 retMask = Parent::packUpdate(conn, mask, stream); 406 407 // InitialUpdate 408 if (stream->writeFlag(mask & InitialUpdateMask)) 409 { 410 // pack extra object's ghost index or scope id if not yet ghosted 411 if (stream->writeFlag(dynamic_cast<NetObject*>(mExtra) != 0)) 412 { 413 NetObject* net_extra = (NetObject*)mExtra; 414 S32 ghost_idx = conn->getGhostIndex(net_extra); 415 if (stream->writeFlag(ghost_idx != -1)) 416 stream->writeRangedU32(U32(ghost_idx), 0, NetConnection::MaxGhostCount); 417 else 418 { 419 if (stream->writeFlag(net_extra->getScopeId() > 0)) 420 { 421 stream->writeInt(net_extra->getScopeId(), NetObject::SCOPE_ID_BITS); 422 } 423 } 424 } 425 426 stream->write(time_factor); 427 428 GameConnection* gconn = dynamic_cast<GameConnection*>(conn); 429 bool zoned_in = (gconn) ? gconn->isZonedIn() : false; 430 if (stream->writeFlag(zoned_in)) 431 pack_constraint_info(conn, stream); 432 } 433 434 // StateEvent or SyncEvent 435 if (stream->writeFlag((mask & StateEventMask) || (mask & SyncEventMask))) 436 { 437 stream->write(marks_mask); 438 stream->write(effect_state); 439 stream->write(effect_elapsed); 440 } 441 442 // SyncEvent 443 bool do_sync_event = ((mask & SyncEventMask) && !(mask & InitialUpdateMask)); 444 if (stream->writeFlag(do_sync_event)) 445 { 446 pack_constraint_info(conn, stream); 447 } 448 449 check_packet_usage(conn, stream, mark_stream_pos, "afxEffectron:"); 450 AssertISV(stream->isValid(), "afxEffectron::packUpdate(): write failure occurred, possibly caused by packet-size overrun."); 451 452 return retMask; 453} 454 455//~~~~~~~~~~~~~~~~~~~~// 456 457void afxEffectron::unpackUpdate(NetConnection * conn, BitStream * stream) 458{ 459 Parent::unpackUpdate(conn, stream); 460 461 bool initial_update = false; 462 bool zoned_in = true; 463 bool do_sync_event = false; 464 U8 new_marks_mask = 0; 465 U8 new_state = INACTIVE_STATE; 466 F32 new_elapsed = 0; 467 468 // InitialUpdate Only 469 if (stream->readFlag()) 470 { 471 initial_update = true; 472 473 // extra sent 474 if (stream->readFlag()) 475 { 476 // cleanup? 477 if (stream->readFlag()) // is ghost_idx 478 { 479 S32 ghost_idx = stream->readRangedU32(0, NetConnection::MaxGhostCount); 480 mExtra = dynamic_cast<SimObject*>(conn->resolveGhost(ghost_idx)); 481 } 482 else 483 { 484 if (stream->readFlag()) // has scope_id 485 { 486 // JTF NOTE: U16 extra_scope_id = stream->readInt(NetObject::SCOPE_ID_BITS); 487 stream->readInt(NetObject::SCOPE_ID_BITS); 488 } 489 } 490 } 491 492 stream->read(&time_factor); 493 494 // if client is marked as fully zoned in 495 if ((zoned_in = stream->readFlag()) == true) 496 { 497 unpack_constraint_info(conn, stream); 498 init_constraints(); 499 } 500 } 501 502 // StateEvent or SyncEvent 503 // this state data is sent for both state-events and 504 // sync-events 505 if (stream->readFlag()) 506 { 507 stream->read(&new_marks_mask); 508 stream->read(&new_state); 509 stream->read(&new_elapsed); 510 511 marks_mask = new_marks_mask; 512 } 513 514 // SyncEvent 515 do_sync_event = stream->readFlag(); 516 if (do_sync_event) 517 { 518 unpack_constraint_info(conn, stream); 519 init_constraints(); 520 } 521 522 //~~~~~~~~~~~~~~~~~~~~// 523 524 if (!zoned_in) 525 effect_state = LATE_STATE; 526 527 // need to adjust state info to get all synced up with spell on server 528 if (do_sync_event && !initial_update) 529 sync_client(new_marks_mask, new_state, new_elapsed); 530} 531 532void afxEffectron::sync_with_clients() 533{ 534 setMaskBits(SyncEventMask); 535} 536 537//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 538// private 539 540bool afxEffectron::state_expired() 541{ 542 afxPhrase* phrase = (effect_state == ACTIVE_STATE) ? active_phrase : NULL; 543 544 if (phrase) 545 { 546 if (phrase->expired(effect_elapsed)) 547 return (!phrase->recycle(effect_elapsed)); 548 return false; 549 } 550 551 return true; 552} 553 554void afxEffectron::init_constraints() 555{ 556 if (constraints_initialized) 557 { 558 //Con::printf("CONSTRAINTS ALREADY INITIALIZED"); 559 return; 560 } 561 562 Vector<afxConstraintDef> defs; 563 datablock->gatherConstraintDefs(defs); 564 565 constraint_mgr->initConstraintDefs(defs, isServerObject()); 566 567 if (isServerObject()) 568 { 569 // find local camera 570 camera_cons_obj = get_camera(); 571 if (camera_cons_obj) 572 camera_cons_id = constraint_mgr->setReferenceObject(CAMERA_CONS, camera_cons_obj); 573 } 574 else // if (isClientObject()) 575 { 576 // find local camera 577 camera_cons_obj = get_camera(); 578 if (camera_cons_obj) 579 camera_cons_id = constraint_mgr->setReferenceObject(CAMERA_CONS, camera_cons_obj); 580 581 // find local listener 582 Point3F listener_pos; 583 listener_pos = SFX->getListener().getTransform().getPosition(); 584 listener_cons_id = constraint_mgr->setReferencePoint(LISTENER_CONS, listener_pos); 585 } 586 587 constraint_mgr->adjustProcessOrdering(this); 588 589 constraints_initialized = true; 590} 591 592void afxEffectron::init_scoping() 593{ 594 if (scoping_initialized) 595 { 596 //Con::printf("SCOPING ALREADY INITIALIZED"); 597 return; 598 } 599 600 if (isServerObject()) 601 { 602 if (explicit_clients.size() > 0) 603 { 604 for (U32 i = 0; i < explicit_clients.size(); i++) 605 explicit_clients[i]->objectLocalScopeAlways(this); 606 } 607 else 608 { 609 mNetFlags.set(Ghostable); 610 setScopeAlways(); 611 } 612 scoping_initialized = true; 613 } 614} 615 616void afxEffectron::setup_active_fx() 617{ 618 active_phrase = new afxPhrase(isServerObject(), /*willStop=*/true); 619 620 if (active_phrase) 621 { 622 active_phrase->init(datablock->fx_list, datablock->duration, this, time_factor, datablock->n_loops); 623 } 624} 625 626bool afxEffectron::cleanup_over() 627{ 628 if (active_phrase && !active_phrase->isEmpty()) 629 return false; 630 631 return true; 632} 633 634void afxEffectron::inflictDamage(const char * label, const char* flavor, SimObjectId target_id, 635 F32 amount, U8 n, F32 ad_amount, F32 radius, Point3F pos, F32 impulse) 636{ 637 char *posArg = Con::getArgBuffer(64); 638 dSprintf(posArg, 64, "%f %f %f", pos.x, pos.y, pos.z); 639 Con::executef(exeblock, "onDamage", 640 getIdString(), 641 label, 642 flavor, 643 Con::getIntArg(target_id), 644 Con::getFloatArg(amount), 645 Con::getIntArg(n), 646 posArg, 647 Con::getFloatArg(ad_amount), 648 Con::getFloatArg(radius), 649 Con::getFloatArg(impulse)); 650} 651 652//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 653// private 654 655void afxEffectron::process_server() 656{ 657 if (effect_state != INACTIVE_STATE) 658 effect_elapsed += TickSec; 659 660 U8 pending_state = effect_state; 661 662 // check for state changes 663 switch (effect_state) 664 { 665 case INACTIVE_STATE: 666 if (marks_mask & MARK_ACTIVATE) 667 pending_state = ACTIVE_STATE; 668 break; 669 case ACTIVE_STATE: 670 if (marks_mask & MARK_INTERRUPT) 671 pending_state = CLEANUP_STATE; 672 else if (marks_mask & MARK_SHUTDOWN) 673 pending_state = CLEANUP_STATE; 674 else if (state_expired()) 675 pending_state = CLEANUP_STATE; 676 break; 677 case CLEANUP_STATE: 678 if (cleanup_over()) 679 pending_state = DONE_STATE; 680 break; 681 } 682 683 if (effect_state != pending_state) 684 change_state_s(pending_state); 685 686 if (effect_state == INACTIVE_STATE) 687 return; 688 689 //--------------------------// 690 691 // sample the constraints 692 constraint_mgr->sample(TickSec, Platform::getVirtualMilliseconds()); 693 694 if (active_phrase) 695 active_phrase->update(TickSec, effect_elapsed); 696} 697 698void afxEffectron::change_state_s(U8 pending_state) 699{ 700 if (effect_state == pending_state) 701 return; 702 703 switch (effect_state) 704 { 705 case INACTIVE_STATE: 706 break; 707 case ACTIVE_STATE: 708 leave_active_state_s(); 709 break; 710 case CLEANUP_STATE: 711 break; 712 case DONE_STATE: 713 break; 714 } 715 716 effect_state = pending_state; 717 718 switch (pending_state) 719 { 720 case INACTIVE_STATE: 721 break; 722 case ACTIVE_STATE: 723 enter_active_state_s(); 724 break; 725 case CLEANUP_STATE: 726 enter_cleanup_state_s(); 727 break; 728 case DONE_STATE: 729 enter_done_state_s(); 730 break; 731 } 732} 733 734void afxEffectron::enter_done_state_s() 735{ 736 postEvent(DEACTIVATE_EVENT); 737 738 F32 done_time = effect_elapsed; 739 740 if (active_phrase) 741 { 742 F32 phrase_done; 743 if (active_phrase->willStop() && active_phrase->isInfinite()) 744 phrase_done = effect_elapsed + active_phrase->calcAfterLife(); 745 else 746 phrase_done = active_phrase->calcDoneTime(); 747 if (phrase_done > done_time) 748 done_time = phrase_done; 749 } 750 751 F32 time_left = done_time - effect_elapsed; 752 if (time_left < 0) 753 time_left = 0; 754 755 Sim::postEvent(this, new ObjectDeleteEvent, Sim::getCurrentTime() + time_left*1000 + 500); 756 757 // CALL SCRIPT afxEffectronData::onDeactivate(%eff) 758 Con::executef(exeblock, "onDeactivate", getIdString()); 759} 760 761void afxEffectron::enter_active_state_s() 762{ 763 // stamp constraint-mgr starting time 764 constraint_mgr->setStartTime(Platform::getVirtualMilliseconds()); 765 effect_elapsed = 0; 766 767 setup_dynamic_constraints(); 768 769 // start casting effects 770 setup_active_fx(); 771 if (active_phrase) 772 active_phrase->start(effect_elapsed, effect_elapsed); 773} 774 775void afxEffectron::leave_active_state_s() 776{ 777 if (active_phrase) 778 active_phrase->stop(effect_elapsed); 779} 780 781void afxEffectron::enter_cleanup_state_s() 782{ 783 postEvent(SHUTDOWN_EVENT); 784} 785 786//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 787// private 788 789void afxEffectron::process_client(F32 dt) 790{ 791 effect_elapsed += dt; 792 793 U8 pending_state = effect_state; 794 795 // check for state changes 796 switch (effect_state) 797 { 798 case INACTIVE_STATE: 799 if (marks_mask & MARK_ACTIVATE) 800 pending_state = ACTIVE_STATE; 801 break; 802 case ACTIVE_STATE: 803 if (marks_mask & MARK_INTERRUPT) 804 pending_state = CLEANUP_STATE; 805 else if (marks_mask & MARK_SHUTDOWN) 806 pending_state = CLEANUP_STATE; 807 else if (state_expired()) 808 pending_state = CLEANUP_STATE; 809 break; 810 case CLEANUP_STATE: 811 if (cleanup_over()) 812 pending_state = DONE_STATE; 813 break; 814 } 815 816 if (effect_state != pending_state) 817 change_state_c(pending_state); 818 819 if (effect_state == INACTIVE_STATE) 820 return; 821 822 //--------------------------// 823 824 // update the listener constraint position 825 if (!listener_cons_id.undefined()) 826 { 827 Point3F listener_pos; 828 listener_pos = SFX->getListener().getTransform().getPosition(); 829 constraint_mgr->setReferencePoint(listener_cons_id, listener_pos); 830 } 831 832 // find local camera position 833 Point3F cam_pos; 834 SceneObject* current_cam = get_camera(&cam_pos); 835 836 // detect camera changes 837 if (!camera_cons_id.undefined() && current_cam != camera_cons_obj) 838 { 839 constraint_mgr->setReferenceObject(camera_cons_id, current_cam); 840 camera_cons_obj = current_cam; 841 } 842 843 // sample the constraints 844 constraint_mgr->sample(dt, Platform::getVirtualMilliseconds(), (current_cam) ? &cam_pos : 0); 845 846 // update active effects lists 847 if (active_phrase) 848 active_phrase->update(dt, effect_elapsed); 849} 850 851void afxEffectron::change_state_c(U8 pending_state) 852{ 853 if (effect_state == pending_state) 854 return; 855 856 switch (effect_state) 857 { 858 case INACTIVE_STATE: 859 break; 860 case ACTIVE_STATE: 861 leave_active_state_c(); 862 break; 863 case CLEANUP_STATE: 864 break; 865 case DONE_STATE: 866 break; 867 } 868 869 effect_state = pending_state; 870 871 switch (pending_state) 872 { 873 case INACTIVE_STATE: 874 break; 875 case ACTIVE_STATE: 876 enter_active_state_c(effect_elapsed); 877 break; 878 case CLEANUP_STATE: 879 break; 880 case DONE_STATE: 881 break; 882 } 883} 884 885void afxEffectron::enter_active_state_c(F32 starttime) 886{ 887 // stamp constraint-mgr starting time 888 constraint_mgr->setStartTime(Platform::getVirtualMilliseconds() - (U32)(effect_elapsed*1000)); 889 ///effect_elapsed = 0; 890 891 setup_dynamic_constraints(); 892 893 setup_active_fx(); 894 if (active_phrase) 895 active_phrase->start(starttime, effect_elapsed); 896} 897 898void afxEffectron::leave_active_state_c() 899{ 900 if (active_phrase) 901 active_phrase->stop(effect_elapsed); 902} 903 904void afxEffectron::sync_client(U16 marks, U8 state, F32 elapsed) 905{ 906 //Con::printf("SYNC marks=%d old_state=%d state=%d elapsed=%g", 907 // marks, effect_state, state, elapsed); 908 909 if (effect_state != LATE_STATE) 910 return; 911 912 marks_mask = marks; 913 914 // don't want to be started on late zoning clients 915 if (!datablock->exec_on_new_clients) 916 { 917 effect_state = DONE_STATE; 918 } 919 920 // it looks like we're ghosting pretty late and 921 // should just return to the inactive state. 922 else if (marks & (MARK_INTERRUPT | MARK_DEACTIVATE | MARK_SHUTDOWN)) 923 { 924 effect_state = DONE_STATE; 925 } 926 927 // it looks like we should be in the active state. 928 else if (marks & MARK_ACTIVATE) 929 { 930 effect_state = ACTIVE_STATE; 931 effect_elapsed = elapsed; 932 enter_active_state_c(0.0); 933 } 934} 935 936//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 937// public: 938 939void afxEffectron::postEvent(U8 event) 940{ 941 setMaskBits(StateEventMask); 942 943 switch (event) 944 { 945 case ACTIVATE_EVENT: 946 marks_mask |= MARK_ACTIVATE; 947 break; 948 case SHUTDOWN_EVENT: 949 marks_mask |= MARK_SHUTDOWN; 950 break; 951 case DEACTIVATE_EVENT: 952 marks_mask |= MARK_DEACTIVATE; 953 break; 954 case INTERRUPT_EVENT: 955 marks_mask |= MARK_INTERRUPT; 956 break; 957 } 958} 959 960//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 961 962void afxEffectron::finish_startup() 963{ 964 init_constraints(); 965 init_scoping(); 966 postEvent(afxEffectron::ACTIVATE_EVENT); 967} 968 969// static 970afxEffectron* 971afxEffectron::start_effect(afxEffectronData* datablock, SimObject* extra) 972{ 973 AssertFatal(datablock != NULL, "Datablock is missing."); 974 975 afxEffectronData* exeblock = datablock; 976 977 SimObject* param_holder = new SimObject(); 978 if (!param_holder->registerObject()) 979 { 980 Con::errorf("afxEffectron: failed to register parameter object."); 981 delete param_holder; 982 return 0; 983 } 984 985 param_holder->assignDynamicFieldsFrom(datablock, arcaneFX::sParameterFieldPrefix); 986 if (extra) 987 { 988 // copy dynamic fields from the extra object to the param holder 989 param_holder->assignDynamicFieldsFrom(extra, arcaneFX::sParameterFieldPrefix); 990 } 991 992 // CALL SCRIPT afxEffectronData::onPreactivate(%params, %extra) 993 const char* result = Con::executef(datablock, "onPreactivate", 994 Con::getIntArg(param_holder->getId()), 995 (extra) ? Con::getIntArg(extra->getId()) : ""); 996 if (result && result[0] != '\0' && !dAtob(result)) 997 { 998#if defined(TORQUE_DEBUG) 999 Con::warnf("afxEffectron: onPreactivate() returned false, effect aborted."); 1000#endif 1001 Sim::postEvent(param_holder, new ObjectDeleteEvent, Sim::getCurrentTime()); 1002 return 0; 1003 } 1004 1005 // make a temp datablock clone if there are substitutions 1006 if (datablock->getSubstitutionCount() > 0) 1007 { 1008 datablock = new afxEffectronData(*exeblock, true); 1009 exeblock->performSubstitutions(datablock, param_holder); 1010 } 1011 1012 // create a new effectron instance 1013 afxEffectron* eff = new afxEffectron(true); 1014 eff->setDataBlock(datablock); 1015 eff->exeblock = exeblock; 1016 eff->setExtra(extra); 1017 1018 // copy dynamic fields from the param holder to the effectron 1019 eff->assignDynamicFieldsFrom(param_holder, arcaneFX::sParameterFieldPrefix); 1020 Sim::postEvent(param_holder, new ObjectDeleteEvent, Sim::getCurrentTime()); 1021 1022 // register 1023 if (!eff->registerObject()) 1024 { 1025 Con::errorf("afxEffectron: failed to register effectron instance."); 1026 Sim::postEvent(eff, new ObjectDeleteEvent, Sim::getCurrentTime()); 1027 return 0; 1028 } 1029 registerForCleanup(eff); 1030 1031 eff->activate(); 1032 1033 return eff; 1034} 1035 1036bool afxEffectron::activationCallInit(bool postponed) 1037{ 1038 if (postponed && (!started_with_newop || !postpone_activation)) 1039 { 1040 Con::errorf("afxEffectron::activate() -- activate() is only required when creating an effectron with the \"new\" operator " 1041 "and the postponeActivation field is set to \"true\"."); 1042 return false; 1043 } 1044 1045 return true; 1046} 1047 1048void afxEffectron::activate() 1049{ 1050 // separating the final part of startup allows the calling script 1051 // to make certain types of calls on the returned effectron that 1052 // need to happen prior to constraint initialization. 1053 Sim::postEvent(this, new EffectronFinishStartupEvent, Sim::getCurrentTime()); 1054 1055 // CALL SCRIPT afxEffectronData::onActivate(%eff) 1056 Con::executef(exeblock, "onActivate", getIdString()); 1057} 1058 1059//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 1060// console functions 1061 1062DefineEngineMethod(afxEffectron, setTimeFactor, void, (float factor),, 1063 "Sets the time-factor for the effectron.\n\n" 1064 "@ingroup AFX") 1065{ 1066 object->setTimeFactor(factor); 1067} 1068 1069DefineEngineMethod(afxEffectron, interrupt, void, (),, 1070 "Interrupts and deletes a running effectron.\n\n" 1071 "@ingroup AFX") 1072{ 1073 object->postEvent(afxEffectron::INTERRUPT_EVENT); 1074} 1075 1076DefineEngineMethod(afxEffectron, activate, void, (),, 1077 "Activates an effectron that was started with postponeActivation=true.\n\n" 1078 "@ingroup AFX") 1079{ 1080 if (object->activationCallInit(true)) 1081 object->activate(); 1082} 1083 1084DefineEngineFunction(startEffectron, S32, (afxEffectronData* datablock, const char* constraintSource, const char* constraintName, SimObject* extra), 1085 (nullAsType<afxEffectronData*>(), nullAsType<const char*>(), nullAsType<const char*>(), nullAsType<SimObject*>()), 1086 1087 "Instantiates the effectron defined by datablock.\n\n" 1088 "@ingroup AFX") 1089{ 1090 if (!datablock) 1091 { 1092 Con::errorf("startEffectron() -- missing valid datablock."); 1093 return 0; 1094 } 1095 1096 // 1097 // Start the Effectron 1098 // 1099 afxEffectron* eff = afxEffectron::start_effect(datablock, extra); 1100 1101 // 1102 // Create a constraint from arguments (if specified). 1103 // 1104 if (eff) 1105 { 1106 if (constraintSource && constraintName) 1107 { 1108 if (!eff->addConstraint(constraintSource, constraintName)) 1109 Con::errorf("startEffectron() -- failed to find constraint object [%s].", constraintSource); 1110 } 1111 } 1112 1113 // 1114 // Return the ID (or 0 if start failed). 1115 // 1116 return (eff) ? eff->getId() : 0; 1117} 1118 1119//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// 1120 1121