Torque3D Documentation / _generateds / afxSelectron.cpp

afxSelectron.cpp

Engine/source/afx/afxSelectron.cpp

More...

Classes:

Public Defines

define
myOffset(field) (field, )

Public Functions

ConsoleDocClass(afxSelectron , "@brief A choreographer <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> selection <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\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(afxSelectronData , "@brief Defines the properties of an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxSelectronData.\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(startSelectron , S32 , (SceneObject *selectedObj, unsigned int subcode, SimObject *extra) , (nullAsType< SceneObject * >(), 0, nullAsType< SimObject * >()) , "Instantiates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selectron.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxSelectron , 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">selectron.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxSelectron , setTimeFactor , void , (float factor) , (1.0f) , "Sets the time factor of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selectron.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxSelectron , stopSelectron , void , () , "Stops and deletes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> running <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selectron.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxSelectronData , reset , void , () , "Resets <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> selectron datablock during <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">reload.\n\n</a>" "@ingroup AFX" )
expand_fx_list(afxEffectList & fx_list, const char * tag)

Detailed Description

Public Defines

myOffset(field) (field, )

Public Functions

ConsoleDocClass(afxSelectron , "@brief A choreographer <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> selection <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\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(afxSelectronData , "@brief Defines the properties of an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxSelectronData.\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(startSelectron , S32 , (SceneObject *selectedObj, unsigned int subcode, SimObject *extra) , (nullAsType< SceneObject * >(), 0, nullAsType< SimObject * >()) , "Instantiates <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selectron.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxSelectron , 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">selectron.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxSelectron , setTimeFactor , void , (float factor) , (1.0f) , "Sets the time factor of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selectron.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxSelectron , stopSelectron , void , () , "Stops and deletes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> running <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selectron.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxSelectronData , reset , void , () , "Resets <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> selectron datablock during <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">reload.\n\n</a>" "@ingroup AFX" )

expand_fx_list(afxEffectList & fx_list, const char * tag)

IMPLEMENT_CO_DATABLOCK_V1(afxSelectronData )

IMPLEMENT_CO_NETOBJECT_V1(afxSelectron )

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