Torque3D Documentation / _generateds / afxMagicSpell.cpp

afxMagicSpell.cpp

Engine/source/afx/afxMagicSpell.cpp

More...

Classes:

Public Defines

define
myOffset(field) (field, )

Public Functions

ConsoleDocClass(afxMagicSpell , "@brief A magic spell 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(afxMagicSpellData , "@brief Defines the properties of an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMagicSpell.\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(castSpell , S32 , (afxMagicSpellData *datablock, ShapeBase *caster, SceneObject *target, SimObject *extra) , (nullAsType< afxMagicSpellData * >(), nullAsType< ShapeBase * >(), nullAsType< SceneObject * >(), nullAsType< SimObject * >()) , "Instantiates the magic spell defined by datablock and cast by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">caster.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpell , getCaster , S32 , () , "Returns ID of the spell's caster <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpell , getImpactedObject , S32 , () , "Returns ID of impacted-object <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spell.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpell , getMissile , S32 , () , "Returns ID of the spell's magic-missile <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpell , getTarget , S32 , () , "Returns ID of the spell's target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpell , interrupt , void , () , "Interrupts and deletes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> running magic <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spell.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpell , interruptStage , void , () , "Interrupts the current stage of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> magic spell causing it <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> move onto the next <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">one.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpellData , pushCastingEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's casting <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpellData , pushDeliveryEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's delivery <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpellData , pushImpactEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's impact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpellData , pushLaunchEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's launch <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpellData , pushLingerEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's linger <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )
DefineEngineMethod(afxMagicSpellData , reset , void , () , "Resets <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell datablock during <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">reload.\n\n</a>" "@ingroup AFX" )
DefineEngineStringlyVariadicMethod(afxMagicSpell , setTimeFactor , void , 3 , 4 , "(F32 factor) or (string phase, <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> factor)" "Sets the time-factor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the spell, either overall or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specific <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phrase.\n\n</a>" " @ingroup AFX" )
expand_fx_list(afxEffectList & fx_list, const char * tag)
IMPLEMENT_CALLBACK(afxMagicSpellData , onActivate , void , (afxMagicSpell *spell, ShapeBase *caster, SceneObject *target) , (spell, caster, target) , "Called when the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">starts.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(afxMagicSpellData , onDamage , void , (afxMagicSpell *spell, const char *label, const char *flaver, U32 target_id, F32 amount, U8 n, Point3F pos, F32 ad_amount, F32 radius, F32 impulse) , (spell, label, flaver, target_id, amount, n, pos, ad_amount, radius, impulse) , "Called when the spell deals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">damage.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(afxMagicSpellData , onDeactivate , void , (afxMagicSpell *spell) , (spell) , "Called when the spell ends <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">naturally.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(afxMagicSpellData , onImpact , void , (afxMagicSpell *spell, ShapeBase *caster, SceneObject *impacted, Point3F pos, Point3F normal) , (spell, caster, impacted, pos, normal) , "Called at the spell's missile impact marking the end of the deliver stage and the start of the linger <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">stage.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(afxMagicSpellData , onInterrupt , void , (afxMagicSpell *spell, ShapeBase *caster) , (spell, caster) , "Called when the spell ends unnaturally due <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">interruption.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(afxMagicSpellData , onLaunch , void , (afxMagicSpell *spell, ShapeBase *caster, SceneObject *target, afxMagicMissile *missile) , (spell, caster, target, missile) , "Called when the spell's casting stage ends and the delivery stage <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">begins.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
IMPLEMENT_CALLBACK(afxMagicSpellData , onPreactivate , bool , (SimObject *param_holder, ShapeBase *caster, SceneObject *target, SimObject *extra) , (param_holder, caster, target, extra) , "Called during spell casting before spell instance is fully <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">created.\n</a>" )
IMPLEMENT_GLOBAL_CALLBACK(DisplayScreenMessage , void , (GameConnection *client, const char *message) , (client, message) , "Called <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> screen <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">message.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
IMPLEMENT_GLOBAL_CALLBACK(onCastingEnd , void , () , () , "A callout called on clients by spells when the casting stage <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ends.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
IMPLEMENT_GLOBAL_CALLBACK(onCastingProgressUpdate , void , (F32 frac) , (frac) , "A callout called periodically on clients by spells <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> indicate casting <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">progress.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
IMPLEMENT_GLOBAL_CALLBACK(onCastingStart , void , () , () , "A callout called on clients by spells when the casting stage <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">begins.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )

Detailed Description

Public Defines

myOffset(field) (field, )

Public Functions

ConsoleDocClass(afxMagicSpell , "@brief A magic spell 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(afxMagicSpellData , "@brief Defines the properties of an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMagicSpell.\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(castSpell , S32 , (afxMagicSpellData *datablock, ShapeBase *caster, SceneObject *target, SimObject *extra) , (nullAsType< afxMagicSpellData * >(), nullAsType< ShapeBase * >(), nullAsType< SceneObject * >(), nullAsType< SimObject * >()) , "Instantiates the magic spell defined by datablock and cast by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">caster.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpell , activate , void , () )

DefineEngineMethod(afxMagicSpell , getCaster , S32 , () , "Returns ID of the spell's caster <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpell , getImpactedObject , S32 , () , "Returns ID of impacted-object <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spell.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpell , getMissile , S32 , () , "Returns ID of the spell's magic-missile <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpell , getTarget , S32 , () , "Returns ID of the spell's target <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpell , interrupt , void , () , "Interrupts and deletes <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> running magic <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">spell.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpell , interruptStage , void , () , "Interrupts the current stage of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> magic spell causing it <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> move onto the next <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">one.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpellData , pushCastingEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's casting <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpellData , pushDeliveryEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's delivery <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpellData , pushImpactEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's impact <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpellData , pushLaunchEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's launch <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )

DefineEngineMethod(afxMagicSpellData , pushLingerEffect , void , (afxEffectBaseData *effect) , "Adds an effect (wrapper or group) <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> spell's linger <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phase.\n\n</a>" "@ingroup AFX" )

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

DefineEngineStringlyVariadicMethod(afxMagicSpell , setTimeFactor , void , 3 , 4 , "(F32 factor) or (string phase, <a href="/coding/file/types_8h/#types_8h_1a841d3674577a1e86afdc2f4845f46c4b">F32</a> factor)" "Sets the time-factor <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the spell, either overall or <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specific <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">phrase.\n\n</a>" " @ingroup AFX" )

expand_fx_list(afxEffectList & fx_list, const char * tag)

IMPLEMENT_CALLBACK(afxMagicSpellData , onActivate , void , (afxMagicSpell *spell, ShapeBase *caster, SceneObject *target) , (spell, caster, target) , "Called when the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">starts.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(afxMagicSpellData , onDamage , void , (afxMagicSpell *spell, const char *label, const char *flaver, U32 target_id, F32 amount, U8 n, Point3F pos, F32 ad_amount, F32 radius, F32 impulse) , (spell, label, flaver, target_id, amount, n, pos, ad_amount, radius, impulse) , "Called when the spell deals <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">damage.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(afxMagicSpellData , onDeactivate , void , (afxMagicSpell *spell) , (spell) , "Called when the spell ends <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">naturally.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(afxMagicSpellData , onImpact , void , (afxMagicSpell *spell, ShapeBase *caster, SceneObject *impacted, Point3F pos, Point3F normal) , (spell, caster, impacted, pos, normal) , "Called at the spell's missile impact marking the end of the deliver stage and the start of the linger <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">stage.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(afxMagicSpellData , onInterrupt , void , (afxMagicSpell *spell, ShapeBase *caster) , (spell, caster) , "Called when the spell ends unnaturally due <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">interruption.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(afxMagicSpellData , onLaunch , void , (afxMagicSpell *spell, ShapeBase *caster, SceneObject *target, afxMagicMissile *missile) , (spell, caster, target, missile) , "Called when the spell's casting stage ends and the delivery stage <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">begins.\n</a>" "@param spell the spell <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

IMPLEMENT_CALLBACK(afxMagicSpellData , onPreactivate , bool , (SimObject *param_holder, ShapeBase *caster, SceneObject *target, SimObject *extra) , (param_holder, caster, target, extra) , "Called during spell casting before spell instance is fully <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">created.\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(afxMagicSpellData )

IMPLEMENT_CO_NETOBJECT_V1(afxMagicSpell )

IMPLEMENT_GLOBAL_CALLBACK(DisplayScreenMessage , void , (GameConnection *client, const char *message) , (client, message) , "Called <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> screen <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">message.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )

IMPLEMENT_GLOBAL_CALLBACK(onCastingEnd , void , () , () , "A callout called on clients by spells when the casting stage <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ends.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )

IMPLEMENT_GLOBAL_CALLBACK(onCastingProgressUpdate , void , (F32 frac) , (frac) , "A callout called periodically on clients by spells <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> indicate casting <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">progress.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )

IMPLEMENT_GLOBAL_CALLBACK(onCastingStart , void , () , () , "A callout called on clients by spells when the casting stage <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">begins.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )

   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#include "T3D/containerQuery.h"
  34
  35#include "afx/afxChoreographer.h"
  36#include "afx/afxPhrase.h"
  37#include "afx/afxMagicSpell.h"
  38
  39//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  40// afxMagicSpellData::ewValidator
  41//
  42// When any of the effect list fields (addCastingEffect, etc.) are set, this validator
  43// intercepts the value and adds it to the appropriate effects list. One validator is
  44// created for each effect list and an id is used to identify which list to add the effect
  45// to.
  46//
  47void afxMagicSpellData::ewValidator::validateType(SimObject* object, void* typePtr)
  48{
  49  afxMagicSpellData* spelldata = dynamic_cast<afxMagicSpellData*>(object);
  50  afxEffectBaseData** ew = (afxEffectBaseData**)(typePtr);
  51
  52  if (spelldata && ew)
  53  {
  54    switch (id)
  55    {
  56    case CASTING_PHRASE:
  57      spelldata->mCasting_fx_list.push_back(*ew);
  58      break;
  59    case LAUNCH_PHRASE:
  60      spelldata->mLaunch_fx_list.push_back(*ew);
  61      break;
  62    case DELIVERY_PHRASE:
  63      spelldata->mDelivery_fx_list.push_back(*ew);
  64      break;
  65    case IMPACT_PHRASE:
  66      spelldata->mImpact_fx_list.push_back(*ew);
  67      break;
  68    case LINGER_PHRASE:
  69      spelldata->mLinger_fx_list.push_back(*ew);
  70      break;
  71    }
  72    *ew = 0;
  73  }
  74}
  75
  76//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  77
  78class SpellFinishStartupEvent : public SimEvent
  79{
  80public:
  81  void process(SimObject* obj)
  82  {
  83     afxMagicSpell* spell = dynamic_cast<afxMagicSpell*>(obj);
  84     if (spell)
  85       spell->finish_startup();
  86  }
  87};
  88
  89//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  90//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  91// afxMagicSpellData
  92
  93IMPLEMENT_CO_DATABLOCK_V1(afxMagicSpellData);
  94
  95ConsoleDocClass( afxMagicSpellData,
  96   "@brief Defines the properties of an afxMagicSpell.\n\n"
  97
  98   "@ingroup afxChoreographers\n"
  99   "@ingroup AFX\n"
 100   "@ingroup Datablocks\n"
 101);
 102
 103IMPLEMENT_CALLBACK( afxMagicSpellData, onDamage, void,
 104   (afxMagicSpell* spell, const char* label, const char* flaver, U32 target_id, F32 amount, U8 n, Point3F pos, F32 ad_amount, F32 radius, F32 impulse),
 105   (spell, label, flaver, target_id, amount, n, pos, ad_amount, radius, impulse),
 106   "Called when the spell deals damage.\n"
 107   "@param spell the spell object\n" );
 108
 109IMPLEMENT_CALLBACK( afxMagicSpellData, onDeactivate, void, (afxMagicSpell* spell), (spell),
 110   "Called when the spell ends naturally.\n"
 111   "@param spell the spell object\n" );
 112
 113IMPLEMENT_CALLBACK( afxMagicSpellData, onInterrupt, void, (afxMagicSpell* spell, ShapeBase* caster), (spell, caster),
 114   "Called when the spell ends unnaturally due to an interruption.\n"
 115   "@param spell the spell object\n" );
 116
 117IMPLEMENT_CALLBACK( afxMagicSpellData, onLaunch, void,
 118   (afxMagicSpell* spell, ShapeBase* caster, SceneObject* target, afxMagicMissile* missile),
 119   (spell, caster, target, missile),
 120   "Called when the spell's casting stage ends and the delivery stage begins.\n"
 121   "@param spell the spell object\n" );
 122
 123IMPLEMENT_CALLBACK( afxMagicSpellData, onImpact, void,
 124   (afxMagicSpell* spell, ShapeBase* caster, SceneObject* impacted, Point3F pos, Point3F normal),
 125   (spell, caster, impacted, pos, normal),
 126   "Called at the spell's missile impact marking the end of the deliver stage and the start of the linger stage.\n"
 127   "@param spell the spell object\n" );
 128
 129IMPLEMENT_CALLBACK( afxMagicSpellData, onPreactivate, bool,
 130   (SimObject* param_holder, ShapeBase* caster, SceneObject* target, SimObject* extra),
 131   (param_holder, caster, target, extra),
 132   "Called during spell casting before spell instance is fully created.\n");
 133
 134IMPLEMENT_CALLBACK( afxMagicSpellData, onActivate, void,
 135   (afxMagicSpell* spell, ShapeBase* caster, SceneObject* target),
 136   (spell, caster, target),
 137   "Called when the spell starts.\n"
 138   "@param spell the spell object\n" );
 139
 140//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 141
 142afxMagicSpellData::afxMagicSpellData()
 143{
 144  mCasting_dur = 0.0f;
 145  mDelivery_dur = 0.0f;
 146  mLinger_dur = 0.0f;
 147
 148  mNum_casting_loops = 1;
 149  mNum_delivery_loops = 1;
 150  mNum_linger_loops = 1;
 151
 152  mExtra_casting_time = 0.0f;
 153  mExtra_delivery_time = 0.0f;
 154  mExtra_linger_time = 0.0f;
 155
 156  // interrupt flags
 157  mDo_move_interrupts = true;
 158  mMove_interrupt_speed = 2.0f;
 159
 160  // delivers projectile spells
 161  mMissile_db = 0;
 162  mLaunch_on_server_signal = false;
 163  mPrimary_target_types = PlayerObjectType;
 164
 165  // dummy entry holds effect-wrapper pointer while a special validator
 166  // grabs it and adds it to an appropriate effects list
 167  mDummy_fx_entry = NULL;
 168
 169  // marked true if datablock ids need to
 170  // be converted into pointers
 171  mDo_id_convert = false;
 172}
 173
 174afxMagicSpellData::afxMagicSpellData(const afxMagicSpellData& other, bool temp_clone) : afxChoreographerData(other, temp_clone)
 175{
 176  mCasting_dur = other.mCasting_dur;
 177  mDelivery_dur = other.mDelivery_dur;
 178  mLinger_dur = other.mLinger_dur;
 179  mNum_casting_loops = other.mNum_casting_loops;
 180  mNum_delivery_loops = other.mNum_delivery_loops;
 181  mNum_linger_loops = other.mNum_linger_loops;
 182  mExtra_casting_time = other.mExtra_casting_time;
 183  mExtra_delivery_time = other.mExtra_delivery_time;
 184  mExtra_linger_time = other.mExtra_linger_time;
 185  mDo_move_interrupts = other.mDo_move_interrupts;
 186  mMove_interrupt_speed = other.mMove_interrupt_speed;
 187  mMissile_db = other.mMissile_db;
 188  mLaunch_on_server_signal = other.mLaunch_on_server_signal;
 189  mPrimary_target_types = other.mPrimary_target_types;
 190
 191  mDummy_fx_entry = other.mDummy_fx_entry;
 192  mDo_id_convert = other.mDo_id_convert;
 193
 194  mCasting_fx_list = other.mCasting_fx_list;
 195  mLaunch_fx_list = other.mLaunch_fx_list;
 196  mDelivery_fx_list = other.mDelivery_fx_list;
 197  mImpact_fx_list = other.mImpact_fx_list;
 198  mLinger_fx_list = other.mLinger_fx_list;
 199}
 200
 201void afxMagicSpellData::reloadReset()
 202{
 203  mCasting_fx_list.clear();
 204  mLaunch_fx_list.clear();
 205  mDelivery_fx_list.clear();
 206  mImpact_fx_list.clear();
 207  mLinger_fx_list.clear();
 208}
 209
 210#define myOffset(field) Offset(field, afxMagicSpellData)
 211
 212void afxMagicSpellData::initPersistFields()
 213{
 214  static ewValidator _castingPhrase(CASTING_PHRASE);
 215  static ewValidator _launchPhrase(LAUNCH_PHRASE);
 216  static ewValidator _deliveryPhrase(DELIVERY_PHRASE);
 217  static ewValidator _impactPhrase(IMPACT_PHRASE);
 218  static ewValidator _lingerPhrase(LINGER_PHRASE);
 219
 220  // for each effect list, dummy_fx_entry is set and then a validator adds it to the appropriate effects list
 221
 222  addGroup("Casting Stage");
 223  addField("castingDur",            TypeF32,        myOffset(mCasting_dur),
 224    "...");
 225  addField("numCastingLoops",       TypeS32,        myOffset(mNum_casting_loops),
 226    "...");
 227  addField("extraCastingTime",      TypeF32,        myOffset(mExtra_casting_time),
 228    "...");
 229  addFieldV("addCastingEffect", TYPEID<afxEffectBaseData>(), Offset(mDummy_fx_entry, afxMagicSpellData), &_castingPhrase,
 230    "...");
 231  endGroup("Casting Stage");
 232
 233  addGroup("Delivery Stage");
 234  addField("deliveryDur",           TypeF32,        myOffset(mDelivery_dur),
 235    "...");
 236  addField("numDeliveryLoops",      TypeS32,        myOffset(mNum_delivery_loops),
 237    "...");
 238  addField("extraDeliveryTime",     TypeF32,        myOffset(mExtra_delivery_time),
 239    "...");
 240  addFieldV("addLaunchEffect", TYPEID<afxEffectBaseData>(), Offset(mDummy_fx_entry, afxMagicSpellData), &_launchPhrase,
 241    "...");
 242  addFieldV("addDeliveryEffect", TYPEID<afxEffectBaseData>(), Offset(mDummy_fx_entry, afxMagicSpellData), &_deliveryPhrase,
 243    "...");
 244  endGroup("Delivery Stage");
 245
 246  addGroup("Linger Stage");
 247  addField("lingerDur",             TypeF32,        myOffset(mLinger_dur),
 248    "...");
 249  addField("numLingerLoops",        TypeS32,        myOffset(mNum_linger_loops),
 250    "...");
 251  addField("extraLingerTime",       TypeF32,        myOffset(mExtra_linger_time),
 252    "...");
 253  addFieldV("addImpactEffect", TYPEID<afxEffectBaseData>(), Offset(mDummy_fx_entry, afxMagicSpellData), &_impactPhrase,
 254    "...");
 255  addFieldV("addLingerEffect", TYPEID<afxEffectBaseData>(), Offset(mDummy_fx_entry, afxMagicSpellData), &_lingerPhrase,
 256    "...");
 257  endGroup("Linger Stage");
 258
 259  // interrupt flags
 260  addField("allowMovementInterrupts", TypeBool,     myOffset(mDo_move_interrupts),
 261    "...");
 262  addField("movementInterruptSpeed",  TypeF32,      myOffset(mMove_interrupt_speed),
 263    "...");
 264
 265  // delivers projectile spells
 266  addField("missile",               TYPEID<afxMagicMissileData>(), myOffset(mMissile_db),
 267    "...");
 268  addField("launchOnServerSignal",  TypeBool,                   myOffset(mLaunch_on_server_signal),
 269    "...");
 270  addField("primaryTargetTypes",    TypeS32,                    myOffset(mPrimary_target_types),
 271    "...");
 272
 273
 274  Parent::initPersistFields();
 275
 276  // disallow some field substitutions
 277  onlyKeepClearSubstitutions("missile"); // subs resolving to "~~", or "~0" are OK
 278  disableFieldSubstitutions("addCastingEffect");
 279  disableFieldSubstitutions("addLaunchEffect");
 280  disableFieldSubstitutions("addDeliveryEffect");
 281  disableFieldSubstitutions("addImpactEffect");
 282  disableFieldSubstitutions("addLingerEffect");
 283}
 284
 285bool afxMagicSpellData::onAdd()
 286{
 287  if (Parent::onAdd() == false)
 288    return false;
 289
 290  if (mMissile_db != NULL && mDelivery_dur == 0.0)
 291    mDelivery_dur = -1;
 292
 293  return true;
 294}
 295
 296void afxMagicSpellData::pack_fx(BitStream* stream, const afxEffectList& fx, bool packed)
 297{
 298  stream->writeInt(fx.size(), EFFECTS_PER_PHRASE_BITS);
 299  for (int i = 0; i < fx.size(); i++)
 300    writeDatablockID(stream, fx[i], packed);
 301}
 302
 303void afxMagicSpellData::unpack_fx(BitStream* stream, afxEffectList& fx)
 304{
 305  fx.clear();
 306  S32 n_fx = stream->readInt(EFFECTS_PER_PHRASE_BITS);
 307  for (int i = 0; i < n_fx; i++)
 308    fx.push_back((afxEffectWrapperData*)(uintptr_t)readDatablockID(stream));
 309}
 310
 311void afxMagicSpellData::packData(BitStream* stream)
 312{
 313   Parent::packData(stream);
 314
 315  stream->write(mCasting_dur);
 316  stream->write(mDelivery_dur);
 317  stream->write(mLinger_dur);
 318  //
 319  stream->write(mNum_casting_loops);
 320  stream->write(mNum_delivery_loops);
 321  stream->write(mNum_linger_loops);
 322  //
 323  stream->write(mExtra_casting_time);
 324  stream->write(mExtra_delivery_time);
 325  stream->write(mExtra_linger_time);
 326
 327  stream->writeFlag(mDo_move_interrupts);
 328  stream->write(mMove_interrupt_speed);
 329
 330  writeDatablockID(stream, mMissile_db, mPacked);
 331  stream->write(mLaunch_on_server_signal);
 332  stream->write(mPrimary_target_types);
 333
 334  pack_fx(stream, mCasting_fx_list, mPacked);
 335  pack_fx(stream, mLaunch_fx_list, mPacked);
 336  pack_fx(stream, mDelivery_fx_list, mPacked);
 337  pack_fx(stream, mImpact_fx_list, mPacked);
 338  pack_fx(stream, mLinger_fx_list, mPacked);
 339}
 340
 341void afxMagicSpellData::unpackData(BitStream* stream)
 342{
 343  Parent::unpackData(stream);
 344
 345  stream->read(&mCasting_dur);
 346  stream->read(&mDelivery_dur);
 347  stream->read(&mLinger_dur);
 348  //
 349  stream->read(&mNum_casting_loops);
 350  stream->read(&mNum_delivery_loops);
 351  stream->read(&mNum_linger_loops);
 352  //
 353  stream->read(&mExtra_casting_time);
 354  stream->read(&mExtra_delivery_time);
 355  stream->read(&mExtra_linger_time);
 356
 357  mDo_move_interrupts = stream->readFlag();
 358  stream->read(&mMove_interrupt_speed);
 359
 360  mMissile_db = (afxMagicMissileData*)(uintptr_t)readDatablockID(stream);
 361  stream->read(&mLaunch_on_server_signal);
 362  stream->read(&mPrimary_target_types);
 363
 364  mDo_id_convert = true;
 365  unpack_fx(stream, mCasting_fx_list);
 366  unpack_fx(stream, mLaunch_fx_list);
 367  unpack_fx(stream, mDelivery_fx_list);
 368  unpack_fx(stream, mImpact_fx_list);
 369  unpack_fx(stream, mLinger_fx_list);
 370}
 371
 372bool afxMagicSpellData::writeField(StringTableEntry fieldname, const char* value)
 373{
 374   if (!Parent::writeField(fieldname, value))
 375      return false;
 376
 377   // don't write the dynamic array fields
 378   if( fieldname == StringTable->insert("addCastingEffect") )
 379      return false;
 380   if( fieldname == StringTable->insert("addLaunchEffect") )
 381      return false;
 382   if( fieldname == StringTable->insert("addDeliveryEffect") )
 383      return false;
 384   if( fieldname == StringTable->insert("addImpactEffect") )
 385      return false;
 386   if( fieldname == StringTable->insert("addLingerEffect") )
 387      return false;
 388
 389   return true;
 390}
 391
 392inline void expand_fx_list(afxEffectList& fx_list, const char* tag)
 393{
 394  for (S32 i = 0; i < fx_list.size(); i++)
 395  {
 396    SimObjectId db_id = SimObjectId((uintptr_t)fx_list[i]);
 397    if (db_id != 0)
 398    {
 399      // try to convert id to pointer
 400      if (!Sim::findObject(db_id, fx_list[i]))
 401      {
 402        Con::errorf(ConsoleLogEntry::General,
 403          "afxMagicSpellData::preload() -- bad datablockId: 0x%x (%s)",
 404          db_id, tag);
 405      }
 406    }
 407  }
 408}
 409
 410bool afxMagicSpellData::preload(bool server, String &errorStr)
 411{
 412  if (!Parent::preload(server, errorStr))
 413    return false;
 414
 415  // Resolve objects transmitted from server
 416  if (!server)
 417  {
 418    if (mDo_id_convert)
 419    {
 420      SimObjectId missile_id = SimObjectId((uintptr_t)mMissile_db);
 421      if (missile_id != 0)
 422      {
 423        // try to convert id to pointer
 424        if (!Sim::findObject(missile_id, mMissile_db))
 425        {
 426          Con::errorf(ConsoleLogEntry::General,
 427            "afxMagicSpellData::preload() -- bad datablockId: 0x%x (missile)",
 428            missile_id);
 429        }
 430      }
 431      expand_fx_list(mCasting_fx_list, "casting");
 432      expand_fx_list(mLaunch_fx_list, "launch");
 433      expand_fx_list(mDelivery_fx_list, "delivery");
 434      expand_fx_list(mImpact_fx_list, "impact");
 435      expand_fx_list(mLinger_fx_list, "linger");
 436     mDo_id_convert = false;
 437    }
 438  }
 439
 440  return true;
 441}
 442
 443void afxMagicSpellData::gatherConstraintDefs(Vector<afxConstraintDef>& defs)
 444{
 445  afxConstraintDef::gather_cons_defs(defs, mCasting_fx_list);
 446  afxConstraintDef::gather_cons_defs(defs, mLaunch_fx_list);
 447  afxConstraintDef::gather_cons_defs(defs, mDelivery_fx_list);
 448  afxConstraintDef::gather_cons_defs(defs, mImpact_fx_list);
 449  afxConstraintDef::gather_cons_defs(defs, mLinger_fx_list);
 450
 451  if (mMissile_db)
 452    mMissile_db->gather_cons_defs(defs);
 453}
 454
 455//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
 456
 457DefineEngineMethod(afxMagicSpellData, reset, void, (),,
 458                   "Resets a spell datablock during reload.\n\n"
 459                   "@ingroup AFX")
 460{
 461  object->reloadReset();
 462}
 463
 464DefineEngineMethod(afxMagicSpellData, pushCastingEffect, void, (afxEffectBaseData* effect),,
 465                   "Adds an effect (wrapper or group) to a spell's casting phase.\n\n"
 466                   "@ingroup AFX")
 467{
 468  if (!effect)
 469  {
 470    Con::errorf(ConsoleLogEntry::General,
 471                "afxMagicSpellData::addCastingEffect() -- "
 472                "missing afxEffectWrapperData.");
 473    return;
 474  }
 475
 476  object->mCasting_fx_list.push_back(effect);
 477}
 478
 479DefineEngineMethod(afxMagicSpellData, pushLaunchEffect, void, (afxEffectBaseData* effect),,
 480                   "Adds an effect (wrapper or group) to a spell's launch phase.\n\n"
 481                   "@ingroup AFX")
 482
 483{
 484  if (!effect)
 485  {
 486    Con::errorf(ConsoleLogEntry::General,
 487                "afxMagicSpellData::addLaunchEffect() -- "
 488                "failed to find afxEffectWrapperData.");
 489    return;
 490  }
 491
 492  object->mLaunch_fx_list.push_back(effect);
 493}
 494
 495DefineEngineMethod(afxMagicSpellData, pushDeliveryEffect, void, (afxEffectBaseData* effect),,
 496                   "Adds an effect (wrapper or group) to a spell's delivery phase.\n\n"
 497                   "@ingroup AFX")
 498
 499{
 500  if (!effect)
 501  {
 502    Con::errorf(ConsoleLogEntry::General,
 503                "afxMagicSpellData::addDeliveryEffect() -- "
 504                "missing afxEffectWrapperData.");
 505    return;
 506  }
 507
 508  object->mDelivery_fx_list.push_back(effect);
 509}
 510
 511DefineEngineMethod(afxMagicSpellData, pushImpactEffect, void, (afxEffectBaseData* effect),,
 512                   "Adds an effect (wrapper or group) to a spell's impact phase.\n\n"
 513                   "@ingroup AFX")
 514
 515{
 516  if (!effect)
 517  {
 518    Con::errorf(ConsoleLogEntry::General,
 519                "afxMagicSpellData::addImpactEffect() -- "
 520                "missing afxEffectWrapperData.");
 521    return;
 522  }
 523
 524  object->mImpact_fx_list.push_back(effect);
 525}
 526
 527DefineEngineMethod(afxMagicSpellData, pushLingerEffect, void, (afxEffectBaseData* effect),,
 528                   "Adds an effect (wrapper or group) to a spell's linger phase.\n\n"
 529                   "@ingroup AFX")
 530
 531{
 532  if (!effect)
 533  {
 534    Con::errorf(ConsoleLogEntry::General,
 535                "afxMagicSpellData::addLingerEffect() -- "
 536                "missing afxEffectWrapperData.");
 537    return;
 538  }
 539
 540  object->mLinger_fx_list.push_back(effect);
 541}
 542
 543
 544//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 545//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 546// afxMagicSpell
 547
 548IMPLEMENT_GLOBAL_CALLBACK( onCastingStart, void, (), (),
 549   "A callout called on clients by spells when the casting stage begins.\n"
 550   "@ingroup AFX\n" );
 551
 552IMPLEMENT_GLOBAL_CALLBACK( onCastingProgressUpdate, void, (F32 frac), (frac),
 553   "A callout called periodically on clients by spells to indicate casting progress.\n"
 554   "@ingroup AFX\n" );
 555
 556IMPLEMENT_GLOBAL_CALLBACK( onCastingEnd, void, (), (),
 557   "A callout called on clients by spells when the casting stage ends.\n"
 558   "@ingroup AFX\n" );
 559
 560//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 561//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 562// CastingPhrase_C
 563//    Subclass of afxPhrase for the client casting phrase.
 564//    This subclass adds handling of the casting progress
 565//    bar in cases where the caster is the client's control
 566//    object.
 567//
 568
 569class CastingPhrase_C : public afxPhrase
 570{
 571  typedef afxPhrase Parent;
 572  ShapeBase*    mCaster;
 573  bool          mNotify_castbar;
 574  F32           mCastbar_progress;
 575public:
 576  /*C*/         CastingPhrase_C(ShapeBase* caster, bool notify_castbar);
 577  virtual void  start(F32 startstamp, F32 timestamp);
 578  virtual void  update(F32 dt, F32 timestamp);
 579  virtual void  stop(F32 timestamp);
 580  virtual void  interrupt(F32 timestamp);
 581};
 582
 583CastingPhrase_C::CastingPhrase_C(ShapeBase* c, bool notify)
 584  : afxPhrase(false, true)
 585{
 586  mCaster = c;
 587  mNotify_castbar = notify;
 588  mCastbar_progress = 0.0f;
 589}
 590
 591void CastingPhrase_C::start(F32 startstamp, F32 timestamp)
 592{
 593  Parent::start(startstamp, timestamp); //START
 594  if (mNotify_castbar)
 595  {
 596    mCastbar_progress = 0.0f;
 597    onCastingStart_callback();
 598  }
 599}
 600
 601void CastingPhrase_C::update(F32 dt, F32 timestamp)
 602{
 603  Parent::update(dt, timestamp);
 604
 605  if (!mNotify_castbar)
 606    return;
 607
 608  if (mDur > 0 && mNum_loops > 0)
 609  {
 610    F32 nfrac = (timestamp - mStartTime)/(mDur*mNum_loops);
 611    if (nfrac - mCastbar_progress > 1.0f/200.0f)
 612    {
 613      mCastbar_progress = (nfrac < 1.0f) ? nfrac : 1.0f;
 614      onCastingProgressUpdate_callback(mCastbar_progress);
 615    }
 616  }
 617}
 618
 619void CastingPhrase_C::stop(F32 timestamp)
 620{
 621  Parent::stop(timestamp);
 622  if (mCastbar_progress)
 623  {
 624    onCastingEnd_callback();
 625    mNotify_castbar = false;
 626  }
 627}
 628
 629void CastingPhrase_C::interrupt(F32 timestamp)
 630{
 631  Parent::interrupt(timestamp);
 632  if (mNotify_castbar)
 633  {
 634    onCastingEnd_callback();
 635   mNotify_castbar = false;
 636  }
 637}
 638
 639//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 640//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 641// some enum to name converters for debugging purposes
 642
 643#ifdef USE_FOR_DEBUG_MESSAGES
 644static char* name_from_state(U8 s)
 645{
 646  switch (s)
 647  {
 648  case afxMagicSpell::INACTIVE_STATE:
 649    return "inactive";
 650  case afxMagicSpell::CASTING_STATE:
 651    return "casting";
 652  case afxMagicSpell::DELIVERY_STATE:
 653    return "delivery";
 654  case afxMagicSpell::LINGER_STATE:
 655    return "linger";
 656  case afxMagicSpell::CLEANUP_STATE:
 657    return "cleanup";
 658  case afxMagicSpell::DONE_STATE:
 659    return "done";
 660  }
 661
 662  return "unknown";
 663}
 664
 665static char* name_from_event(U8 e)
 666{
 667  switch (e)
 668  {
 669  case afxMagicSpell::NULL_EVENT:
 670    return "null";
 671  case afxMagicSpell::ACTIVATE_EVENT:
 672    return "activate";
 673  case afxMagicSpell::LAUNCH_EVENT:
 674    return "launch";
 675  case afxMagicSpell::IMPACT_EVENT:
 676    return "impact";
 677  case afxMagicSpell::SHUTDOWN_EVENT:
 678    return "shutdown";
 679  case afxMagicSpell::DEACTIVATE_EVENT:
 680    return "deactivate";
 681  case afxMagicSpell::INTERRUPT_PHASE_EVENT:
 682    return "interrupt_phase";
 683  case afxMagicSpell::INTERRUPT_SPELL_EVENT:
 684    return "interrupt_spell";
 685  }
 686
 687  return "unknown";
 688}
 689#endif
 690
 691//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 692//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 693// afxMagicSpell
 694
 695IMPLEMENT_CO_NETOBJECT_V1(afxMagicSpell);
 696
 697ConsoleDocClass( afxMagicSpell,
 698   "@brief A magic spell effects choreographer.\n\n"
 699
 700   "@ingroup afxChoreographers\n"
 701   "@ingroup AFX\n"
 702);
 703
 704// static
 705StringTableEntry  afxMagicSpell::CASTER_CONS;
 706StringTableEntry  afxMagicSpell::TARGET_CONS;
 707StringTableEntry  afxMagicSpell::MISSILE_CONS;
 708StringTableEntry  afxMagicSpell::CAMERA_CONS;
 709StringTableEntry  afxMagicSpell::LISTENER_CONS;
 710StringTableEntry  afxMagicSpell::IMPACT_POINT_CONS;
 711StringTableEntry  afxMagicSpell::IMPACTED_OBJECT_CONS;
 712
 713void afxMagicSpell::init()
 714{
 715  // setup static predefined constraint names
 716  if (CASTER_CONS == 0)
 717  {
 718    CASTER_CONS = StringTable->insert("caster");
 719    TARGET_CONS = StringTable->insert("target");
 720    MISSILE_CONS = StringTable->insert("missile");
 721    CAMERA_CONS = StringTable->insert("camera");
 722    LISTENER_CONS = StringTable->insert("listener");
 723    IMPACT_POINT_CONS = StringTable->insert("impactPoint");
 724    IMPACTED_OBJECT_CONS = StringTable->insert("impactedObject");
 725  }
 726
 727  // afxMagicSpell is always in scope, however effects
 728  // do their own scoping in that they will shut off if
 729  // their position constraint leaves scope.
 730  //
 731  //   note -- ghosting is delayed until constraint
 732  //           initialization is done.
 733  //
 734  //mNetFlags.set(Ghostable | ScopeAlways);
 735  mNetFlags.clear(Ghostable | ScopeAlways);
 736
 737  mDatablock = NULL;
 738  mExeblock = NULL;
 739  mMissile_db = NULL;
 740
 741  mCaster = NULL;
 742  mTarget = NULL;
 743
 744  mCaster_field = NULL;
 745  mTarget_field = NULL;
 746
 747  mCaster_scope_id = 0;
 748  mTarget_scope_id = 0;
 749  mTarget_is_shape = false;
 750
 751  mConstraints_initialized = false;
 752  mScoping_initialized = false;
 753
 754  mSpell_state = (U8) INACTIVE_STATE;
 755  mSpell_elapsed = 0;
 756
 757  // define named constraints
 758  constraint_mgr->defineConstraint(OBJECT_CONSTRAINT,  CASTER_CONS);
 759  constraint_mgr->defineConstraint(OBJECT_CONSTRAINT,  TARGET_CONS);
 760  constraint_mgr->defineConstraint(OBJECT_CONSTRAINT, MISSILE_CONS);
 761  constraint_mgr->defineConstraint(CAMERA_CONSTRAINT, CAMERA_CONS);
 762  constraint_mgr->defineConstraint(POINT_CONSTRAINT,  LISTENER_CONS);
 763  constraint_mgr->defineConstraint(POINT_CONSTRAINT,  IMPACT_POINT_CONS);
 764  constraint_mgr->defineConstraint(OBJECT_CONSTRAINT,  IMPACTED_OBJECT_CONS);
 765
 766  for (S32 i = 0; i < NUM_PHRASES; i++)
 767  {
 768    mPhrases[i] = NULL;
 769    mTfactors[i] = 1.0f;
 770  }
 771
 772  mNotify_castbar = false;
 773  mOverall_time_factor = 1.0f;
 774
 775  mCamera_cons_obj = 0;
 776
 777  mMarks_mask = 0;
 778
 779  mMissile = NULL;
 780  mMissile_is_armed = false;
 781  mImpacted_obj = NULL;
 782  mImpact_pos.zero();
 783  mImpact_norm.set(0,0,1);
 784  mImpacted_scope_id = 0;
 785  mImpacted_is_shape = false;
 786}
 787
 788afxMagicSpell::afxMagicSpell()
 789{
 790  started_with_newop = true;
 791  init();
 792}
 793
 794afxMagicSpell::afxMagicSpell(ShapeBase* caster, SceneObject* target)
 795{
 796  started_with_newop = false;
 797  init();
 798
 799  mCaster = caster;
 800  if (caster)
 801  {
 802    mCaster_field = caster;
 803    deleteNotify(caster);
 804    processAfter(caster);
 805  }
 806
 807  mTarget = target;
 808  if (target)
 809  {
 810    mTarget_field = target;
 811    deleteNotify(target);
 812  }
 813}
 814
 815afxMagicSpell::~afxMagicSpell()
 816{
 817  for (S32 i = 0; i < NUM_PHRASES; i++)
 818  {
 819    if (mPhrases[i])
 820    {
 821      mPhrases[i]->interrupt(mSpell_elapsed);
 822      delete mPhrases[i];
 823    }
 824  }
 825
 826  if (mMissile)
 827    mMissile->deleteObject();
 828
 829  if (mMissile_db && mMissile_db->isTempClone())
 830  {
 831    delete mMissile_db;
 832    mMissile_db = 0;
 833  }
 834
 835   if (mDatablock && mDatablock->isTempClone())
 836   {
 837     delete mDatablock;
 838    mDatablock = 0;
 839   }
 840}
 841
 842//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 843
 844// STANDARD OVERLOADED METHODS //
 845
 846bool afxMagicSpell::onNewDataBlock(GameBaseData* dptr, bool reload)
 847{
 848  mDatablock = dynamic_cast<afxMagicSpellData*>(dptr);
 849  if (!mDatablock || !Parent::onNewDataBlock(dptr, reload))
 850    return false;
 851
 852  if (isServerObject() && started_with_newop)
 853  {
 854    // copy dynamic fields from the datablock but
 855    // don't replace fields with a value
 856    assignDynamicFieldsFrom(dptr, arcaneFX::sParameterFieldPrefix, true);
 857  }
 858
 859  mExeblock = mDatablock;
 860  mMissile_db = mDatablock->mMissile_db;
 861
 862  if (isClientObject())
 863  {
 864    // make a temp datablock clone if there are substitutions
 865    if (mDatablock->getSubstitutionCount() > 0)
 866    {
 867      afxMagicSpellData* orig_db = mDatablock;
 868     mDatablock = new afxMagicSpellData(*orig_db, true);
 869     mExeblock = orig_db;
 870     mMissile_db = mDatablock->mMissile_db;
 871      // Don't perform substitutions yet, the spell's dynamic fields haven't
 872      // arrived yet and the substitutions may refer to them. Hold off and do
 873      // in in the onAdd() method.
 874    }
 875  }
 876  else if (started_with_newop)
 877  {
 878    // make a temp datablock clone if there are substitutions
 879    if (mDatablock->getSubstitutionCount() > 0)
 880    {
 881      afxMagicSpellData* orig_db = mDatablock;
 882     mDatablock = new afxMagicSpellData(*orig_db, true);
 883     mExeblock = orig_db;
 884      orig_db->performSubstitutions(mDatablock, this, ranking);
 885     mMissile_db = mDatablock->mMissile_db;
 886    }
 887  }
 888
 889  return true;
 890}
 891
 892void afxMagicSpell::processTick(const Move* m)
 893{
 894  Parent::processTick(m);
 895
 896  // don't process moves or client ticks
 897  if (m != 0 || isClientObject())
 898    return;
 899
 900  process_server();
 901}
 902
 903void afxMagicSpell::advanceTime(F32 dt)
 904{
 905  Parent::advanceTime(dt);
 906
 907  process_client(dt);
 908}
 909
 910bool afxMagicSpell::onAdd()
 911{
 912  if (!Parent::onAdd())
 913    return false ;
 914
 915  if (isClientObject())
 916  {
 917    if (mDatablock->isTempClone())
 918    {
 919      afxMagicSpellData* orig_db = (afxMagicSpellData*)mExeblock;
 920      orig_db->performSubstitutions(mDatablock, this, ranking);
 921     mMissile_db = mDatablock->mMissile_db;
 922      mNotify_castbar = (mNotify_castbar && (mDatablock->mCasting_dur > 0.0f));
 923    }
 924  }
 925  else if (started_with_newop && !postpone_activation)
 926  {
 927    if (!activationCallInit())
 928      return false;
 929    activate();
 930  }
 931
 932  return true ;
 933}
 934
 935void afxMagicSpell::onRemove()
 936{
 937  Parent::onRemove();
 938}
 939
 940void afxMagicSpell::onDeleteNotify(SimObject* obj)
 941{
 942  // caster deleted?
 943  ShapeBase* shape = dynamic_cast<ShapeBase*>(obj);
 944  if (shape == mCaster)
 945  {
 946    clearProcessAfter();
 947   mCaster = NULL;
 948    mCaster_field = NULL;
 949   mCaster_scope_id = 0;
 950  }
 951
 952  // target deleted?
 953  SceneObject* scene_obj = dynamic_cast<SceneObject*>(obj);
 954  if (scene_obj == mTarget)
 955  {
 956    mTarget = NULL;
 957    mTarget_field = NULL;
 958    mTarget_scope_id = 0;
 959    mTarget_is_shape = false;
 960  }
 961
 962  // impacted_obj deleted?
 963  if (scene_obj == mImpacted_obj)
 964  {
 965    mImpacted_obj = NULL;
 966    mImpacted_scope_id = 0;
 967    mImpacted_is_shape = false;
 968  }
 969
 970  // missile deleted?
 971  afxMagicMissile* missile = dynamic_cast<afxMagicMissile*>(obj);
 972  if (missile != NULL && missile == mMissile)
 973  {
 974    mMissile = NULL;
 975  }
 976
 977  // something else
 978  Parent::onDeleteNotify(obj);
 979}
 980
 981// static
 982void afxMagicSpell::initPersistFields()
 983{
 984  addField("caster", TYPEID<SimObject>(), Offset(mCaster_field, afxMagicSpell),
 985    "...");
 986  addField("target", TYPEID<SimObject>(), Offset(mTarget_field, afxMagicSpell),
 987    "...");
 988
 989  Parent::initPersistFields();
 990}
 991
 992//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 993
 994void afxMagicSpell::pack_constraint_info(NetConnection* conn, BitStream* stream)
 995{
 996  // pack caster's ghost index or scope id if not yet ghosted
 997  if (stream->writeFlag(mCaster != NULL))
 998  {
 999    S32 ghost_idx = conn->getGhostIndex(mCaster);
1000    if (stream->writeFlag(ghost_idx != -1))
1001      stream->writeRangedU32(U32(ghost_idx), 0, NetConnection::MaxGhostCount);
1002    else
1003    {
1004      bool bit = (mCaster) ? (mCaster->getScopeId() > 0) : false;
1005      if (stream->writeFlag(bit))
1006        stream->writeInt(mCaster->getScopeId(), NetObject::SCOPE_ID_BITS);
1007    }
1008  }
1009
1010  // pack target's ghost index or scope id if not yet ghosted
1011  if (stream->writeFlag(mTarget != NULL))
1012  {
1013    S32 ghost_idx = conn->getGhostIndex(mTarget);
1014    if (stream->writeFlag(ghost_idx != -1))
1015      stream->writeRangedU32(U32(ghost_idx), 0, NetConnection::MaxGhostCount);
1016    else
1017    {
1018      if (stream->writeFlag(mTarget->getScopeId() > 0))
1019      {
1020        stream->writeInt(mTarget->getScopeId(), NetObject::SCOPE_ID_BITS);
1021        stream->writeFlag(dynamic_cast<ShapeBase*>(mTarget) != NULL); // is shape?
1022      }
1023    }
1024  }
1025
1026  Parent::pack_constraint_info(conn, stream);
1027}
1028
1029void afxMagicSpell::unpack_constraint_info(NetConnection* conn, BitStream* stream)
1030{
1031  mCaster = NULL;
1032  mCaster_field = NULL;
1033  mCaster_scope_id = 0;
1034  if (stream->readFlag()) // has caster
1035  {
1036    if (stream->readFlag()) // has ghost_idx
1037    {
1038      S32 ghost_idx = stream->readRangedU32(0, NetConnection::MaxGhostCount);
1039     mCaster = dynamic_cast<ShapeBase*>(conn->resolveGhost(ghost_idx));
1040      if (mCaster)
1041      {
1042        mCaster_field = mCaster;
1043        deleteNotify(mCaster);
1044        processAfter(mCaster);
1045      }
1046    }
1047    else
1048    {
1049      if (stream->readFlag()) // has scope_id (is always a shape)
1050        mCaster_scope_id = stream->readInt(NetObject::SCOPE_ID_BITS);
1051    }
1052  }
1053
1054  mTarget = NULL;
1055  mTarget_field = NULL;
1056  mTarget_scope_id = 0;
1057  mTarget_is_shape = false;
1058  if (stream->readFlag()) // has target
1059  {
1060    if (stream->readFlag()) // has ghost_idx
1061    {
1062      S32 ghost_idx = stream->readRangedU32(0, NetConnection::MaxGhostCount);
1063      mTarget = dynamic_cast<SceneObject*>(conn->resolveGhost(ghost_idx));
1064      if (mTarget)
1065      {
1066        mTarget_field = mTarget;
1067        deleteNotify(mTarget);
1068      }
1069    }
1070    else
1071    {
1072      if (stream->readFlag()) // has scope_id
1073      {
1074        mTarget_scope_id = stream->readInt(NetObject::SCOPE_ID_BITS);
1075        mTarget_is_shape = stream->readFlag(); // is shape?
1076      }
1077    }
1078  }
1079
1080  Parent::unpack_constraint_info(conn, stream);
1081}
1082
1083U32 afxMagicSpell::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
1084{
1085  S32 mark_stream_pos = stream->getCurPos();
1086
1087  U32 retMask = Parent::packUpdate(conn, mask, stream);
1088
1089  // InitialUpdate
1090  if (stream->writeFlag(mask & InitialUpdateMask))
1091  {
1092    // pack extra object's ghost index or scope id if not yet ghosted
1093    if (stream->writeFlag(dynamic_cast<NetObject*>(mExtra) != 0))
1094    {
1095      NetObject* net_extra = (NetObject*)mExtra;
1096      S32 ghost_idx = conn->getGhostIndex(net_extra);
1097      if (stream->writeFlag(ghost_idx != -1))
1098         stream->writeRangedU32(U32(ghost_idx), 0, NetConnection::MaxGhostCount);
1099      else
1100      {
1101        if (stream->writeFlag(net_extra->getScopeId() > 0))
1102        {
1103          stream->writeInt(net_extra->getScopeId(), NetObject::SCOPE_ID_BITS);
1104        }
1105      }
1106    }
1107
1108    // pack initial exec conditions
1109    stream->write(exec_conds_mask);
1110
1111    // flag if this client owns the spellcaster
1112    bool client_owns_caster = is_caster_client(mCaster, dynamic_cast<GameConnection*>(conn));
1113    stream->writeFlag(client_owns_caster);
1114
1115    // pack per-phrase time-factor values
1116    for (S32 i = 0; i < NUM_PHRASES; i++)
1117      stream->write(mTfactors[i]);
1118
1119    // flag if this conn is zoned-in yet
1120    bool zoned_in = client_owns_caster;
1121    if (!zoned_in)
1122    {
1123      GameConnection* gconn = dynamic_cast<GameConnection*>(conn);
1124      zoned_in = (gconn) ? gconn->isZonedIn() : false;
1125    }
1126    if (stream->writeFlag(zoned_in))
1127      pack_constraint_info(conn, stream);
1128  }
1129
1130  // StateEvent or SyncEvent
1131  if (stream->writeFlag((mask & StateEventMask) || (mask & SyncEventMask)))
1132  {
1133    stream->write(mMarks_mask);
1134    stream->write(mSpell_state);
1135    stream->write(state_elapsed());
1136    stream->write(mSpell_elapsed);
1137  }
1138
1139  // SyncEvent
1140  if (stream->writeFlag((mask & SyncEventMask) && !(mask & InitialUpdateMask)))
1141  {
1142    pack_constraint_info(conn, stream);
1143  }
1144
1145  // LaunchEvent
1146  if (stream->writeFlag((mask & LaunchEventMask) && (mMarks_mask & MARK_LAUNCH) && mMissile))
1147  {
1148    F32 vel; Point3F vel_vec;
1149    mMissile->getStartingVelocityValues(vel, vel_vec);
1150    // pack launch vector and velocity
1151    stream->write(vel);
1152    mathWrite(*stream, vel_vec);
1153  }
1154
1155  // ImpactEvent
1156  if (stream->writeFlag(((mask & ImpactEventMask) || (mask & SyncEventMask)) && (mMarks_mask & MARK_IMPACT)))
1157  {
1158    // pack impact objects's ghost index or scope id if not yet ghosted
1159    if (stream->writeFlag(mImpacted_obj != NULL))
1160    {
1161      S32 ghost_idx = conn->getGhostIndex(mImpacted_obj);
1162      if (stream->writeFlag(ghost_idx != -1))
1163        stream->writeRangedU32(U32(ghost_idx), 0, NetConnection::MaxGhostCount);
1164      else
1165      {
1166        if (stream->writeFlag(mImpacted_obj->getScopeId() > 0))
1167        {
1168          stream->writeInt(mImpacted_obj->getScopeId(), NetObject::SCOPE_ID_BITS);
1169          stream->writeFlag(dynamic_cast<ShapeBase*>(mImpacted_obj) != NULL);
1170        }
1171      }
1172    }
1173
1174    // pack impact position and normal
1175    mathWrite(*stream, mImpact_pos);
1176    mathWrite(*stream, mImpact_norm);
1177    stream->write(exec_conds_mask);
1178
1179    ShapeBase* temp_shape;
1180    stream->writeFlag(mCaster != 0 && mCaster->getDamageState() == ShapeBase::Enabled);
1181    temp_shape = dynamic_cast<ShapeBase*>(mTarget);
1182    stream->writeFlag(temp_shape != 0 && temp_shape->getDamageState() == ShapeBase::Enabled);
1183    temp_shape = dynamic_cast<ShapeBase*>(mImpacted_obj);
1184    stream->writeFlag(temp_shape != 0 && temp_shape->getDamageState() == ShapeBase::Enabled);
1185  }
1186
1187  check_packet_usage(conn, stream, mark_stream_pos, "afxMagicSpell:");
1188
1189  AssertISV(stream->isValid(), "afxMagicSpell::packUpdate(): write failure occurred, possibly caused by packet-size overrun.");
1190
1191  return retMask;
1192}
1193
1194//~~~~~~~~~~~~~~~~~~~~//
1195
1196 // CONSTRAINT REMAPPING <<
1197bool afxMagicSpell::remap_builtin_constraint(SceneObject* obj, const char* cons_name)
1198{
1199  StringTableEntry cons_name_ste = StringTable->insert(cons_name);
1200
1201  if (cons_name_ste == CASTER_CONS)
1202    return true;
1203  if (cons_name_ste == TARGET_CONS)
1204  {
1205    if (obj && mTarget && obj != mTarget && !mTarget_cons_id.undefined())
1206    {
1207      mTarget = obj;
1208      constraint_mgr->setReferenceObject(mTarget_cons_id, mTarget);
1209      if (isServerObject())
1210      {
1211        if (mTarget->isScopeable())
1212          constraint_mgr->addScopeableObject(mTarget);
1213      }
1214    }
1215    return true;
1216  }
1217  if (cons_name_ste == MISSILE_CONS)
1218    return true;
1219  if (cons_name_ste == CAMERA_CONS)
1220    return true;
1221  if (cons_name_ste == LISTENER_CONS)
1222    return true;
1223  if (cons_name_ste == IMPACT_POINT_CONS)
1224    return true;
1225  if (cons_name_ste == IMPACTED_OBJECT_CONS)
1226    return true;
1227
1228  return false;
1229}
1230 // CONSTRAINT REMAPPING >>
1231
1232void afxMagicSpell::unpackUpdate(NetConnection * conn, BitStream * stream)
1233{
1234  Parent::unpackUpdate(conn, stream);
1235
1236  bool initial_update = false;
1237  bool zoned_in = true;
1238  bool do_sync_event = false;
1239  U16 new_marks_mask = 0;
1240  U8 new_spell_state = INACTIVE_STATE;
1241  F32 new_state_elapsed = 0;
1242  F32 new_spell_elapsed = 0;;
1243
1244  // InitialUpdate
1245  if (stream->readFlag())
1246  {
1247    initial_update = true;
1248
1249    // extra sent
1250    if (stream->readFlag())
1251    {
1252      // cleanup?
1253      if (stream->readFlag()) // is ghost_idx
1254      {
1255        S32 ghost_idx = stream->readRangedU32(0, NetConnection::MaxGhostCount);
1256      mExtra = dynamic_cast<SimObject*>(conn->resolveGhost(ghost_idx));
1257      }
1258      else
1259      {
1260        if (stream->readFlag()) // has scope_id
1261        {
1262          // JTF NOTE: U16 extra_scope_id = stream->readInt(NetObject::SCOPE_ID_BITS);
1263          stream->readInt(NetObject::SCOPE_ID_BITS);
1264        }
1265      }
1266    }
1267
1268    // unpack initial exec conditions
1269    stream->read(&exec_conds_mask);
1270
1271    // if this is controlling client for the caster,
1272    // enable castbar updates
1273    bool client_owns_caster = stream->readFlag();
1274    if (client_owns_caster)
1275      mNotify_castbar = Con::isFunction("onCastingStart");
1276
1277    // unpack per-phrase time-factor values
1278    for (S32 i = 0; i < NUM_PHRASES; i++)
1279      stream->read(&mTfactors[i]);
1280
1281    // if client is marked as fully zoned in
1282    if ((zoned_in = stream->readFlag()) == true)
1283    {
1284      unpack_constraint_info(conn, stream);
1285      init_constraints();
1286    }
1287  }
1288
1289  // StateEvent or SyncEvent
1290  // this state data is sent for both state-events and
1291  // sync-events
1292  if (stream->readFlag())
1293  {
1294    stream->read(&new_marks_mask);
1295    stream->read(&new_spell_state);
1296    stream->read(&new_state_elapsed);
1297    stream->read(&new_spell_elapsed);
1298    mMarks_mask = new_marks_mask;
1299  }
1300
1301  // SyncEvent
1302  if ((do_sync_event = stream->readFlag()) == true)
1303  {
1304    unpack_constraint_info(conn, stream);
1305    init_constraints();
1306  }
1307
1308  // LaunchEvent
1309  if (stream->readFlag())
1310  {
1311    F32 vel; Point3F vel_vec;
1312    stream->read(&vel);
1313    mathRead(*stream, &vel_vec);
1314    if (mMissile)
1315    {
1316      mMissile->setStartingVelocity(vel);
1317     mMissile->setStartingVelocityVector(vel_vec);
1318    }
1319  }
1320
1321  // ImpactEvent
1322  if (stream->readFlag())
1323  {
1324    if (mImpacted_obj)
1325      clearNotify(mImpacted_obj);
1326    mImpacted_obj = NULL;
1327    mImpacted_scope_id = 0;
1328    mImpacted_is_shape = false;
1329    if (stream->readFlag()) // is impacted_obj
1330    {
1331      if (stream->readFlag()) // is ghost_idx
1332      {
1333        S32 ghost_idx = stream->readRangedU32(0, NetConnection::MaxGhostCount);
1334        mImpacted_obj = dynamic_cast<SceneObject*>(conn->resolveGhost(ghost_idx));
1335        if (mImpacted_obj)
1336          deleteNotify(mImpacted_obj);
1337      }
1338      else
1339      {
1340        if (stream->readFlag()) // has scope_id
1341        {
1342          mImpacted_scope_id = stream->readInt(NetObject::SCOPE_ID_BITS);
1343          mImpacted_is_shape = stream->readFlag(); // is shape?
1344        }
1345      }
1346    }
1347
1348    mathRead(*stream, &mImpact_pos);
1349    mathRead(*stream, &mImpact_norm);
1350    stream->read(&exec_conds_mask);
1351
1352    bool caster_alive = stream->readFlag();
1353    bool target_alive = stream->readFlag();
1354    bool impacted_alive = stream->readFlag();
1355
1356    afxConstraint* cons;
1357    if ((cons = constraint_mgr->getConstraint(mCaster_cons_id)) != 0)
1358      cons->setLivingState(caster_alive);
1359    if ((cons = constraint_mgr->getConstraint(mTarget_cons_id)) != 0)
1360      cons->setLivingState(target_alive);
1361    if ((cons = constraint_mgr->getConstraint(mImpacted_cons_id)) != 0)
1362      cons->setLivingState(impacted_alive);
1363  }
1364
1365  //~~~~~~~~~~~~~~~~~~~~//
1366
1367  if (!zoned_in)
1368    mSpell_state = LATE_STATE;
1369
1370  // need to adjust state info to get all synced up with spell on server
1371  if (do_sync_event && !initial_update)
1372    sync_client(new_marks_mask, new_spell_state, new_state_elapsed, new_spell_elapsed);
1373}
1374
1375void afxMagicSpell::sync_with_clients()
1376{
1377  setMaskBits(SyncEventMask);
1378}
1379
1380//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1381// private
1382
1383bool afxMagicSpell::state_expired()
1384{
1385  afxPhrase* phrase = NULL;
1386
1387  switch (mSpell_state)
1388  {
1389  case CASTING_STATE:
1390    phrase = mPhrases[CASTING_PHRASE];
1391    break;
1392  case DELIVERY_STATE:
1393    phrase = mPhrases[DELIVERY_PHRASE];
1394    break;
1395  case LINGER_STATE:
1396    phrase = mPhrases[LINGER_PHRASE];
1397    break;
1398  }
1399
1400  if (phrase)
1401  {
1402    if (phrase->expired(mSpell_elapsed))
1403      return (!phrase->recycle(mSpell_elapsed));
1404    return false;
1405  }
1406
1407  return true;
1408}
1409
1410F32 afxMagicSpell::state_elapsed()
1411{
1412  afxPhrase* phrase = NULL;
1413
1414  switch (mSpell_state)
1415  {
1416  case CASTING_STATE:
1417    phrase = mPhrases[CASTING_PHRASE];
1418    break;
1419  case DELIVERY_STATE:
1420    phrase = mPhrases[DELIVERY_PHRASE];
1421    break;
1422  case LINGER_STATE:
1423    phrase = mPhrases[LINGER_PHRASE];
1424    break;
1425  }
1426
1427  return (phrase) ? phrase->elapsed(mSpell_elapsed) : 0.0f;
1428}
1429
1430void afxMagicSpell::init_constraints()
1431{
1432  if (mConstraints_initialized)
1433  {
1434    //Con::printf("CONSTRAINTS ALREADY INITIALIZED");
1435    return;
1436  }
1437
1438  Vector<afxConstraintDef> defs;
1439  mDatablock->gatherConstraintDefs(defs);
1440
1441  constraint_mgr->initConstraintDefs(defs, isServerObject());
1442
1443  if (isServerObject())
1444  {
1445    mCaster_cons_id = constraint_mgr->setReferenceObject(CASTER_CONS, mCaster);
1446    mTarget_cons_id = constraint_mgr->setReferenceObject(TARGET_CONS, mTarget);
1447#if defined(AFX_CAP_SCOPE_TRACKING)
1448    if (mCaster && mCaster->isScopeable())
1449      constraint_mgr->addScopeableObject(mCaster);
1450
1451    if (mTarget && mTarget->isScopeable())
1452      constraint_mgr->addScopeableObject(mTarget);
1453#endif
1454
1455    // find local camera
1456   mCamera_cons_obj = get_camera();
1457    if (mCamera_cons_obj)
1458      mCamera_cons_id = constraint_mgr->setReferenceObject(CAMERA_CONS, mCamera_cons_obj);
1459  }
1460  else // if (isClientObject())
1461  {
1462    if (mCaster)
1463      mCaster_cons_id = constraint_mgr->setReferenceObject(CASTER_CONS, mCaster);
1464    else if (mCaster_scope_id > 0)
1465      mCaster_cons_id = constraint_mgr->setReferenceObjectByScopeId(CASTER_CONS, mCaster_scope_id, true);
1466
1467    if (mTarget)
1468      mTarget_cons_id = constraint_mgr->setReferenceObject(TARGET_CONS, mTarget);
1469    else if (mTarget_scope_id > 0)
1470      mTarget_cons_id = constraint_mgr->setReferenceObjectByScopeId(TARGET_CONS, mTarget_scope_id, mTarget_is_shape);
1471
1472    // find local camera
1473   mCamera_cons_obj = get_camera();
1474    if (mCamera_cons_obj)
1475      mCamera_cons_id = constraint_mgr->setReferenceObject(CAMERA_CONS, mCamera_cons_obj);
1476
1477    // find local listener
1478    Point3F listener_pos;
1479    listener_pos = SFX->getListener().getTransform().getPosition();
1480    mListener_cons_id = constraint_mgr->setReferencePoint(LISTENER_CONS, listener_pos);
1481  }
1482
1483  constraint_mgr->adjustProcessOrdering(this);
1484
1485  mConstraints_initialized = true;
1486}
1487
1488void afxMagicSpell::init_scoping()
1489{
1490  if (mScoping_initialized)
1491  {
1492    //Con::printf("SCOPING ALREADY INITIALIZED");
1493    return;
1494  }
1495
1496  if (isServerObject())
1497  {
1498    if (explicit_clients.size() > 0)
1499    {
1500      for (U32 i = 0; i < explicit_clients.size(); i++)
1501        explicit_clients[i]->objectLocalScopeAlways(this);
1502    }
1503    else
1504    {
1505      mNetFlags.set(Ghostable);
1506      setScopeAlways();
1507    }
1508    mScoping_initialized = true;
1509  }
1510}
1511
1512void afxMagicSpell::setup_casting_fx()
1513{
1514  if (isServerObject())
1515    mPhrases[CASTING_PHRASE] = new afxPhrase(isServerObject(), true);
1516  else
1517    mPhrases[CASTING_PHRASE] = new CastingPhrase_C(mCaster, mNotify_castbar);
1518
1519  if (mPhrases[CASTING_PHRASE])
1520    mPhrases[CASTING_PHRASE]->init(mDatablock->mCasting_fx_list, mDatablock->mCasting_dur, this,
1521                                  mTfactors[CASTING_PHRASE], mDatablock->mNum_casting_loops, 0,
1522                                  mDatablock->mExtra_casting_time);
1523}
1524
1525void afxMagicSpell::setup_launch_fx()
1526{
1527  mPhrases[LAUNCH_PHRASE] = new afxPhrase(isServerObject(), false);
1528  if (mPhrases[LAUNCH_PHRASE])
1529    mPhrases[LAUNCH_PHRASE]->init(mDatablock->mLaunch_fx_list, -1, this,
1530                                 mTfactors[LAUNCH_PHRASE], 1);
1531}
1532
1533void afxMagicSpell::setup_delivery_fx()
1534{
1535  mPhrases[DELIVERY_PHRASE] = new afxPhrase(isServerObject(), true);
1536  if (mPhrases[DELIVERY_PHRASE])
1537  {
1538    mPhrases[DELIVERY_PHRASE]->init(mDatablock->mDelivery_fx_list, mDatablock->mDelivery_dur, this,
1539                                   mTfactors[DELIVERY_PHRASE], mDatablock->mNum_delivery_loops, 0,
1540                                   mDatablock->mExtra_delivery_time);
1541  }
1542}
1543
1544void afxMagicSpell::setup_impact_fx()
1545{
1546  mPhrases[IMPACT_PHRASE] = new afxPhrase(isServerObject(), false);
1547  if (mPhrases[IMPACT_PHRASE])
1548  {
1549    mPhrases[IMPACT_PHRASE]->init(mDatablock->mImpact_fx_list, -1, this,
1550                                 mTfactors[IMPACT_PHRASE], 1);
1551  }
1552}
1553
1554void afxMagicSpell::setup_linger_fx()
1555{
1556  mPhrases[LINGER_PHRASE] = new afxPhrase(isServerObject(), true);
1557  if (mPhrases[LINGER_PHRASE])
1558    mPhrases[LINGER_PHRASE]->init(mDatablock->mLinger_fx_list, mDatablock->mLinger_dur, this,
1559                                 mTfactors[LINGER_PHRASE], mDatablock->mNum_linger_loops, 0,
1560                                 mDatablock->mExtra_linger_time);
1561}
1562
1563bool afxMagicSpell::cleanup_over()
1564{
1565  for (S32 i = 0; i < NUM_PHRASES; i++)
1566    if (mPhrases[i] && !mPhrases[i]->isEmpty())
1567      return false;
1568
1569  return true;
1570}
1571
1572//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1573// private
1574//
1575// MISSILE STUFF
1576//
1577
1578void afxMagicSpell::init_missile_s(afxMagicMissileData* mm_db)
1579{
1580  if (mMissile)
1581    clearNotify(mMissile);
1582
1583  // create the missile
1584  mMissile = new afxMagicMissile(true, false);
1585  mMissile->setSubstitutionData(this, ranking);
1586  mMissile->setDataBlock(mm_db);
1587  mMissile->setChoreographer(this);
1588  if (!mMissile->registerObject())
1589  {
1590    Con::errorf("afxMagicSpell: failed to register missile instance.");
1591    delete mMissile;
1592   mMissile = NULL;
1593  }
1594
1595  if (mMissile)
1596  {
1597    deleteNotify(mMissile);
1598    registerForCleanup(mMissile);
1599  }
1600}
1601
1602void afxMagicSpell::launch_missile_s()
1603{
1604  if (mMissile)
1605  {
1606     mMissile->launch();
1607    constraint_mgr->setReferenceObject(MISSILE_CONS, mMissile);
1608  }
1609}
1610
1611void afxMagicSpell::init_missile_c(afxMagicMissileData* mm_db)
1612{
1613  if (mMissile)
1614    clearNotify(mMissile);
1615
1616  // create the missile
1617  mMissile = new afxMagicMissile(false, true);
1618  mMissile->setSubstitutionData(this, ranking);
1619  mMissile->setDataBlock(mm_db);
1620  mMissile->setChoreographer(this);
1621  if (!mMissile->registerObject())
1622  {
1623    Con::errorf("afxMagicSpell: failed to register missile instance.");
1624    delete mMissile;
1625   mMissile = NULL;
1626  }
1627
1628  if (mMissile)
1629  {
1630    deleteNotify(mMissile);
1631    registerForCleanup(mMissile);
1632  }
1633}
1634
1635void afxMagicSpell::launch_missile_c()
1636{
1637  if (mMissile)
1638  {
1639    mMissile->launch();
1640    constraint_mgr->setReferenceObject(MISSILE_CONS, mMissile);
1641  }
1642}
1643
1644bool afxMagicSpell::is_impact_in_water(SceneObject* obj, const Point3F& p)
1645{
1646  // AFX_T3D_BROKEN -- water impact detection is disabled. Look at projectile.
1647  return false;
1648}
1649
1650void afxMagicSpell::impactNotify(const Point3F& p, const Point3F& n, SceneObject* obj)
1651{
1652  if (isClientObject())
1653    return;
1654
1655  ///impact_time_ms = spell_elapsed_ms;
1656  if (mImpacted_obj)
1657      clearNotify(mImpacted_obj);
1658  mImpacted_obj = obj;
1659  mImpact_pos = p;
1660  mImpact_norm = n;
1661
1662  if (mImpacted_obj != NULL)
1663  {
1664    deleteNotify(mImpacted_obj);
1665    exec_conds_mask |= IMPACTED_SOMETHING;
1666    if (mImpacted_obj == mTarget)
1667      exec_conds_mask |= IMPACTED_TARGET;
1668    if (mImpacted_obj->getTypeMask() & mDatablock->mPrimary_target_types)
1669      exec_conds_mask |= IMPACTED_PRIMARY;
1670  }
1671
1672  if (is_impact_in_water(obj, p))
1673    exec_conds_mask |= IMPACT_IN_WATER;
1674
1675  postSpellEvent(IMPACT_EVENT);
1676
1677  if (mMissile)
1678    clearNotify(mMissile);
1679  mMissile = NULL;
1680}
1681
1682void afxMagicSpell::executeScriptEvent(const char* method, afxConstraint* cons,
1683                                        const MatrixF& xfm, const char* data)
1684{
1685  SceneObject* cons_obj = (cons) ? cons->getSceneObject() : NULL;
1686
1687  char *arg_buf = Con::getArgBuffer(256);
1688  Point3F pos;
1689  xfm.getColumn(3,&pos);
1690  AngAxisF aa(xfm);
1691  dSprintf(arg_buf,256,"%g %g %g %g %g %g %g",
1692           pos.x, pos.y, pos.z,
1693           aa.axis.x, aa.axis.y, aa.axis.z, aa.angle);
1694
1695  // CALL SCRIPT afxChoreographerData::method(%spell, %caster, %constraint, %transform, %data)
1696  Con::executef(mExeblock, method,
1697                getIdString(),
1698                (mCaster) ? mCaster->getIdString() : "",
1699                (cons_obj) ? cons_obj->getIdString() : "",
1700                arg_buf,
1701                data);
1702}
1703
1704void afxMagicSpell::inflictDamage(const char * label, const char* flavor, SimObjectId target_id,
1705                                   F32 amount, U8 n, F32 ad_amount, F32 radius, Point3F pos, F32 impulse)
1706{
1707 // Con::printf("INFLICT-DAMAGE label=%s flav=%s id=%d amt=%g n=%d rad=%g pos=(%g %g %g) imp=%g",
1708 //             label, flavor, target_id, amount, n, radius, pos.x, pos.y, pos.z, impulse);
1709
1710  // CALL SCRIPT afxMagicSpellData::onDamage()
1711  //    onDamage(%spell, %label, %type, %damaged_obj, %amount, %count, %pos, %ad_amount,
1712  //             %radius, %impulse)
1713  mDatablock->onDamage_callback(this, label, flavor, target_id, amount, n, pos, ad_amount, radius, impulse);
1714}
1715
1716
1717//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1718// private
1719
1720void afxMagicSpell::process_server()
1721{
1722  if (mSpell_state != INACTIVE_STATE)
1723    mSpell_elapsed += TickSec;
1724
1725  U8 pending_state = mSpell_state;
1726
1727  // check for state changes
1728  switch (mSpell_state)
1729  {
1730
1731  case INACTIVE_STATE:
1732    if (mMarks_mask & MARK_ACTIVATE)
1733      pending_state = CASTING_STATE;
1734    break;
1735
1736  case CASTING_STATE:
1737    if (mDatablock->mCasting_dur > 0.0f && mDatablock->mDo_move_interrupts && is_caster_moving())
1738    {
1739      displayScreenMessage(mCaster, "SPELL INTERRUPTED.");
1740      postSpellEvent(INTERRUPT_SPELL_EVENT);
1741    }
1742    if (mMarks_mask & MARK_INTERRUPT_CASTING)
1743      pending_state = CLEANUP_STATE;
1744    else if (mMarks_mask & MARK_END_CASTING)
1745      pending_state = DELIVERY_STATE;
1746    else if (mMarks_mask & MARK_LAUNCH)
1747      pending_state = DELIVERY_STATE;
1748    else if (state_expired())
1749      pending_state = DELIVERY_STATE;
1750    break;
1751
1752  case DELIVERY_STATE:
1753    if (mMarks_mask & MARK_INTERRUPT_DELIVERY)
1754      pending_state = CLEANUP_STATE;
1755    else if (mMarks_mask & MARK_END_DELIVERY)
1756      pending_state = LINGER_STATE;
1757    else if (mMarks_mask & MARK_IMPACT)
1758      pending_state = LINGER_STATE;
1759    else if (state_expired())
1760      pending_state = LINGER_STATE;
1761    break;
1762
1763  case LINGER_STATE:
1764    if (mMarks_mask & MARK_INTERRUPT_LINGER)
1765      pending_state = CLEANUP_STATE;
1766    else if (mMarks_mask & MARK_END_LINGER)
1767      pending_state = CLEANUP_STATE;
1768    else if (mMarks_mask & MARK_SHUTDOWN)
1769      pending_state = CLEANUP_STATE;
1770    else if (state_expired())
1771      pending_state = CLEANUP_STATE;
1772    break;
1773
1774  case CLEANUP_STATE:
1775    if ((mMarks_mask & MARK_INTERRUPT_CLEANUP) || cleanup_over())
1776      pending_state = DONE_STATE;
1777    break;
1778  }
1779
1780  if (mSpell_state != pending_state)
1781    change_state_s(pending_state);
1782
1783  if (mSpell_state == INACTIVE_STATE)
1784    return;
1785
1786  //--------------------------//
1787
1788  // sample the constraints
1789  constraint_mgr->sample(TickSec, Platform::getVirtualMilliseconds());
1790
1791  for (S32 i = 0; i < NUM_PHRASES; i++)
1792    if (mPhrases[i])
1793      mPhrases[i]->update(TickSec, mSpell_elapsed);
1794
1795  if (mMissile_is_armed)
1796  {
1797    launch_missile_s();
1798   mMissile_is_armed = false;
1799  }
1800}
1801
1802void afxMagicSpell::change_state_s(U8 pending_state)
1803{
1804  if (mSpell_state == pending_state)
1805    return;
1806
1807  // LEAVING THIS STATE
1808  switch (mSpell_state)
1809  {
1810  case INACTIVE_STATE:
1811    break;
1812  case CASTING_STATE:
1813    leave_casting_state_s();
1814    break;
1815  case DELIVERY_STATE:
1816    leave_delivery_state_s();
1817    break;
1818  case LINGER_STATE:
1819    leave_linger_state_s();
1820    break;
1821  case CLEANUP_STATE:
1822    break;
1823  case DONE_STATE:
1824    break;
1825  }
1826
1827  mSpell_state = pending_state;
1828
1829  // ENTERING THIS STATE
1830  switch (pending_state)
1831  {
1832  case INACTIVE_STATE:
1833    break;
1834  case CASTING_STATE:
1835    enter_casting_state_s();
1836    break;
1837  case DELIVERY_STATE:
1838    enter_delivery_state_s();
1839    break;
1840  case LINGER_STATE:
1841    enter_linger_state_s();
1842    break;
1843  case CLEANUP_STATE:
1844    break;
1845  case DONE_STATE:
1846    enter_done_state_s();
1847    break;
1848  }
1849}
1850
1851void afxMagicSpell::enter_done_state_s()
1852{
1853  postSpellEvent(DEACTIVATE_EVENT);
1854
1855  if (mMarks_mask & MARK_INTERRUPTS)
1856  {
1857    Sim::postEvent(this, new ObjectDeleteEvent, Sim::getCurrentTime() + 500);
1858  }
1859  else
1860  {
1861    F32 done_time = mSpell_elapsed;
1862
1863    for (S32 i = 0; i < NUM_PHRASES; i++)
1864    {
1865      if (mPhrases[i])
1866      {
1867        F32 phrase_done;
1868        if (mPhrases[i]->willStop() && mPhrases[i]->isInfinite())
1869          phrase_done = mSpell_elapsed + mPhrases[i]->calcAfterLife();
1870        else
1871          phrase_done = mPhrases[i]->calcDoneTime();
1872        if (phrase_done > done_time)
1873          done_time = phrase_done;
1874      }
1875    }
1876
1877    F32 time_left = done_time - mSpell_elapsed;
1878    if (time_left < 0)
1879      time_left = 0;
1880
1881    Sim::postEvent(this, new ObjectDeleteEvent, Sim::getCurrentTime() + time_left*1000 + 500);
1882  }
1883
1884  // CALL SCRIPT afxMagicSpellData::onDeactivate(%spell)
1885  mDatablock->onDeactivate_callback(this);
1886}
1887
1888void afxMagicSpell::enter_casting_state_s()
1889{
1890  // note - onActivate() is called in cast_spell() instead of here to make sure any
1891  // new time-factor settings resolve before they are sent off to the clients.
1892
1893  // stamp constraint-mgr starting time and reset spell timer
1894  constraint_mgr->setStartTime(Platform::getVirtualMilliseconds());
1895  mSpell_elapsed = 0;
1896
1897  setup_dynamic_constraints();
1898
1899  // start casting effects
1900  setup_casting_fx();
1901  if (mPhrases[CASTING_PHRASE])
1902    mPhrases[CASTING_PHRASE]->start(mSpell_elapsed, mSpell_elapsed);
1903
1904  // initialize missile
1905  if (mMissile_db)
1906  {
1907    mMissile_db = mMissile_db->cloneAndPerformSubstitutions(this, ranking);
1908    init_missile_s(mMissile_db);
1909  }
1910}
1911
1912void afxMagicSpell::leave_casting_state_s()
1913{
1914  if (mPhrases[CASTING_PHRASE])
1915  {
1916    if (mMarks_mask & MARK_INTERRUPT_CASTING)
1917    {
1918      //Con::printf("INTERRUPT CASTING (S)");
1919      mPhrases[CASTING_PHRASE]->interrupt(mSpell_elapsed);
1920    }
1921    else
1922    {
1923      //Con::printf("LEAVING CASTING (S)");
1924      mPhrases[CASTING_PHRASE]->stop(mSpell_elapsed);
1925    }
1926  }
1927
1928  if (mMarks_mask & MARK_INTERRUPT_CASTING)
1929  {
1930    // CALL SCRIPT afxMagicSpellData::onInterrupt(%spell, %caster)
1931    mDatablock->onInterrupt_callback(this, mCaster);
1932  }
1933}
1934
1935void afxMagicSpell::enter_delivery_state_s()
1936{
1937  // CALL SCRIPT afxMagicSpellData::onLaunch(%spell, %caster, %target, %missile)
1938  mDatablock->onLaunch_callback(this, mCaster, mTarget, mMissile);
1939
1940  if (mDatablock->mLaunch_on_server_signal)
1941    postSpellEvent(LAUNCH_EVENT);
1942
1943  mMissile_is_armed = true;
1944
1945  // start launch effects
1946  setup_launch_fx();
1947  if (mPhrases[LAUNCH_PHRASE])
1948    mPhrases[LAUNCH_PHRASE]->start(mSpell_elapsed, mSpell_elapsed); //START
1949
1950  // start delivery effects
1951  setup_delivery_fx();
1952  if (mPhrases[DELIVERY_PHRASE])
1953    mPhrases[DELIVERY_PHRASE]->start(mSpell_elapsed, mSpell_elapsed); //START
1954}
1955
1956void afxMagicSpell::leave_delivery_state_s()
1957{
1958  if (mPhrases[DELIVERY_PHRASE])
1959  {
1960    if (mMarks_mask & MARK_INTERRUPT_DELIVERY)
1961    {
1962      //Con::printf("INTERRUPT DELIVERY (S)");
1963      mPhrases[DELIVERY_PHRASE]->interrupt(mSpell_elapsed);
1964    }
1965    else
1966    {
1967      //Con::printf("LEAVING DELIVERY (S)");
1968      mPhrases[DELIVERY_PHRASE]->stop(mSpell_elapsed);
1969    }
1970  }
1971
1972  if (!mMissile && !(mMarks_mask & MARK_IMPACT))
1973  {
1974    if (mTarget)
1975    {
1976      Point3F p = afxMagicSpell::getShapeImpactPos(mTarget);
1977      Point3F n = Point3F(0,0,1);
1978      impactNotify(p, n, mTarget);
1979    }
1980    else
1981    {
1982      Point3F p = Point3F(0,0,0);
1983      Point3F n = Point3F(0,0,1);
1984      impactNotify(p, n, 0);
1985    }
1986  }
1987}
1988
1989void afxMagicSpell::enter_linger_state_s()
1990{
1991  if (mImpacted_obj)
1992  {
1993    mImpacted_cons_id = constraint_mgr->setReferenceObject(IMPACTED_OBJECT_CONS, mImpacted_obj);
1994#if defined(AFX_CAP_SCOPE_TRACKING)
1995    if (mImpacted_obj->isScopeable())
1996      constraint_mgr->addScopeableObject(mImpacted_obj);
1997#endif
1998  }
1999  else
2000    constraint_mgr->setReferencePoint(IMPACTED_OBJECT_CONS, mImpact_pos, mImpact_norm);
2001  constraint_mgr->setReferencePoint(IMPACT_POINT_CONS, mImpact_pos, mImpact_norm);
2002  constraint_mgr->setReferenceObject(MISSILE_CONS, 0);
2003
2004  // start impact effects
2005  setup_impact_fx();
2006  if (mPhrases[IMPACT_PHRASE])
2007    mPhrases[IMPACT_PHRASE]->start(mSpell_elapsed, mSpell_elapsed); //START
2008
2009  // start linger effects
2010  setup_linger_fx();
2011  if (mPhrases[LINGER_PHRASE])
2012    mPhrases[LINGER_PHRASE]->start(mSpell_elapsed, mSpell_elapsed); //START
2013
2014#if 0 // code temporarily replaced with old callback technique in order to avoid engine bug.
2015  // CALL SCRIPT afxMagicSpellData::onImpact(%spell, %caster, %impactedObj, %impactedPos, %impactedNorm)
2016  mDatablock->onImpact_callback(this, mCaster, mImpacted_obj, mImpact_pos, mImpact_norm);
2017#else
2018  char pos_buf[128];
2019  dSprintf(pos_buf, sizeof(pos_buf), "%g %g %g", mImpact_pos.x, mImpact_pos.y, mImpact_pos.z);
2020  char norm_buf[128];
2021  dSprintf(norm_buf, sizeof(norm_buf), "%g %g %g", mImpact_norm.x, mImpact_norm.y, mImpact_norm.z);
2022  Con::executef(mExeblock, "onImpact", getIdString(),
2023     (mCaster) ? mCaster->getIdString(): "",
2024     (mImpacted_obj) ? mImpacted_obj->getIdString(): "",
2025     pos_buf, norm_buf);
2026#endif
2027}
2028
2029void afxMagicSpell::leave_linger_state_s()
2030{
2031  if (mPhrases[LINGER_PHRASE])
2032  {
2033    if (mMarks_mask & MARK_INTERRUPT_LINGER)
2034    {
2035      //Con::printf("INTERRUPT LINGER (S)");
2036      mPhrases[LINGER_PHRASE]->interrupt(mSpell_elapsed);
2037    }
2038    else
2039    {
2040      //Con::printf("LEAVING LINGER (S)");
2041      mPhrases[LINGER_PHRASE]->stop(mSpell_elapsed);
2042    }
2043  }
2044}
2045
2046
2047
2048//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
2049// private
2050
2051void afxMagicSpell::process_client(F32 dt)
2052{
2053  mSpell_elapsed += dt; //SPELL_ELAPSED
2054
2055  U8 pending_state = mSpell_state;
2056
2057  // check for state changes
2058  switch (mSpell_state)
2059  {
2060  case INACTIVE_STATE:
2061    if (mMarks_mask & MARK_ACTIVATE)
2062      pending_state = CASTING_STATE;
2063    break;
2064  case CASTING_STATE:
2065    if (mMarks_mask & MARK_INTERRUPT_CASTING)
2066      pending_state = CLEANUP_STATE;
2067    else if (mMarks_mask & MARK_END_CASTING)
2068      pending_state = DELIVERY_STATE;
2069    else if (mDatablock->mLaunch_on_server_signal)
2070    {
2071      if (mMarks_mask & MARK_LAUNCH)
2072        pending_state = DELIVERY_STATE;
2073    }
2074    else
2075    {
2076      if (state_expired())
2077        pending_state = DELIVERY_STATE;
2078    }
2079    break;
2080  case DELIVERY_STATE:
2081    if (mMarks_mask & MARK_INTERRUPT_DELIVERY)
2082      pending_state = CLEANUP_STATE;
2083    else if (mMarks_mask & MARK_END_DELIVERY)
2084      pending_state = LINGER_STATE;
2085    else if (mMarks_mask & MARK_IMPACT)
2086      pending_state = LINGER_STATE;
2087    else
2088      state_expired();
2089    break;
2090  case LINGER_STATE:
2091    if (mMarks_mask & MARK_INTERRUPT_LINGER)
2092      pending_state = CLEANUP_STATE;
2093    else if (mMarks_mask & MARK_END_LINGER)
2094      pending_state = CLEANUP_STATE;
2095    else if (mMarks_mask & MARK_SHUTDOWN)
2096      pending_state = CLEANUP_STATE;
2097    else if (state_expired())
2098      pending_state = CLEANUP_STATE;
2099    break;
2100  case CLEANUP_STATE:
2101    if ((mMarks_mask & MARK_INTERRUPT_CLEANUP) || cleanup_over())
2102      pending_state = DONE_STATE;
2103    break;
2104  }
2105
2106  if (mSpell_state != pending_state)
2107    change_state_c(pending_state);
2108
2109  if (mSpell_state == INACTIVE_STATE)
2110    return;
2111
2112  //--------------------------//
2113
2114  // update the listener constraint position
2115  if (!mListener_cons_id.undefined())
2116  {
2117    Point3F listener_pos;
2118    listener_pos = SFX->getListener().getTransform().getPosition();
2119    constraint_mgr->setReferencePoint(mListener_cons_id, listener_pos);
2120  }
2121
2122  // find local camera position
2123  Point3F cam_pos;
2124  SceneObject* current_cam = get_camera(&cam_pos);
2125
2126  // detect camera changes
2127  if (!mCamera_cons_id.undefined() && current_cam != mCamera_cons_obj)
2128  {
2129    constraint_mgr->setReferenceObject(mCamera_cons_id, current_cam);
2130   mCamera_cons_obj = current_cam;
2131  }
2132
2133  // sample the constraints
2134  constraint_mgr->sample(dt, Platform::getVirtualMilliseconds(), (current_cam) ? &cam_pos : 0);
2135
2136  // update active effects lists
2137  for (S32 i = 0; i < NUM_PHRASES; i++)
2138    if (mPhrases[i])
2139      mPhrases[i]->update(dt, mSpell_elapsed);
2140
2141  if (mMissile_is_armed)
2142  {
2143    launch_missile_c();
2144   mMissile_is_armed = false;
2145  }
2146}
2147
2148void afxMagicSpell::change_state_c(U8 pending_state)
2149{
2150  if (mSpell_state == pending_state)
2151    return;
2152
2153  // LEAVING THIS STATE
2154  switch (mSpell_state)
2155  {
2156  case INACTIVE_STATE:
2157    break;
2158  case CASTING_STATE:
2159    leave_casting_state_c();
2160    break;
2161  case DELIVERY_STATE:
2162    leave_delivery_state_c();
2163    break;
2164  case LINGER_STATE:
2165    leave_linger_state_c();
2166    break;
2167  case CLEANUP_STATE:
2168    break;
2169  case DONE_STATE:
2170    break;
2171  }
2172
2173  mSpell_state = pending_state;
2174
2175  // ENTERING THIS STATE
2176  switch (pending_state)
2177  {
2178  case INACTIVE_STATE:
2179    break;
2180  case CASTING_STATE:
2181    enter_casting_state_c(mSpell_elapsed);
2182    break;
2183  case DELIVERY_STATE:
2184    enter_delivery_state_c(mSpell_elapsed);
2185    break;
2186  case LINGER_STATE:
2187    enter_linger_state_c(mSpell_elapsed);
2188    break;
2189  case CLEANUP_STATE:
2190    break;
2191  case DONE_STATE:
2192    break;
2193  }
2194}
2195
2196void afxMagicSpell::enter_casting_state_c(F32 starttime)
2197{
2198  // stamp constraint-mgr starting time
2199  constraint_mgr->setStartTime(Platform::getVirtualMilliseconds() - (U32)(mSpell_elapsed *1000));
2200  //spell_elapsed = 0; //SPELL_ELAPSED
2201
2202  setup_dynamic_constraints();
2203
2204  // start casting effects and castbar
2205  setup_casting_fx();
2206  if (mPhrases[CASTING_PHRASE])
2207    mPhrases[CASTING_PHRASE]->start(starttime, mSpell_elapsed); //START
2208
2209  // initialize missile
2210  if (mMissile_db)
2211  {
2212    mMissile_db = mMissile_db->cloneAndPerformSubstitutions(this, ranking);
2213    init_missile_c(mMissile_db);
2214  }
2215}
2216
2217void afxMagicSpell::leave_casting_state_c()
2218{
2219  if (mPhrases[CASTING_PHRASE])
2220  {
2221    if (mMarks_mask & MARK_INTERRUPT_CASTING)
2222    {
2223      //Con::printf("INTERRUPT CASTING (C)");
2224      mPhrases[CASTING_PHRASE]->interrupt(mSpell_elapsed);
2225    }
2226    else
2227    {
2228      //Con::printf("LEAVING CASTING (C)");
2229      mPhrases[CASTING_PHRASE]->stop(mSpell_elapsed);
2230    }
2231  }
2232}
2233
2234void afxMagicSpell::enter_delivery_state_c(F32 starttime)
2235{
2236  mMissile_is_armed = true;
2237
2238  setup_launch_fx();
2239  if (mPhrases[LAUNCH_PHRASE])
2240    mPhrases[LAUNCH_PHRASE]->start(starttime, mSpell_elapsed); //START
2241
2242  setup_delivery_fx();
2243  if (mPhrases[DELIVERY_PHRASE])
2244    mPhrases[DELIVERY_PHRASE]->start(starttime, mSpell_elapsed); //START
2245}
2246
2247void afxMagicSpell::leave_delivery_state_c()
2248{
2249  if (mMissile)
2250  {
2251    clearNotify(mMissile);
2252   mMissile->deleteObject();
2253   mMissile = NULL;
2254  }
2255
2256  if (mPhrases[DELIVERY_PHRASE])
2257  {
2258    if (mMarks_mask & MARK_INTERRUPT_DELIVERY)
2259    {
2260      //Con::printf("INTERRUPT DELIVERY (C)");
2261      mPhrases[DELIVERY_PHRASE]->interrupt(mSpell_elapsed);
2262    }
2263    else
2264    {
2265      //Con::printf("LEAVING DELIVERY (C)");
2266      mPhrases[DELIVERY_PHRASE]->stop(mSpell_elapsed);
2267    }
2268  }
2269}
2270
2271void afxMagicSpell::enter_linger_state_c(F32 starttime)
2272{
2273  if (mImpacted_obj)
2274    mImpacted_cons_id = constraint_mgr->setReferenceObject(IMPACTED_OBJECT_CONS, mImpacted_obj);
2275  else if (mImpacted_scope_id > 0)
2276    mImpacted_cons_id = constraint_mgr->setReferenceObjectByScopeId(IMPACTED_OBJECT_CONS, mImpacted_scope_id, mImpacted_is_shape);
2277  else
2278    constraint_mgr->setReferencePoint(IMPACTED_OBJECT_CONS, mImpact_pos, mImpact_norm);
2279  constraint_mgr->setReferencePoint(IMPACT_POINT_CONS, mImpact_pos, mImpact_norm);
2280  constraint_mgr->setReferenceObject(MISSILE_CONS, 0);
2281
2282  setup_impact_fx();
2283  if (mPhrases[IMPACT_PHRASE])
2284    mPhrases[IMPACT_PHRASE]->start(starttime, mSpell_elapsed); //START
2285
2286  setup_linger_fx();
2287  if (mPhrases[LINGER_PHRASE])
2288  {
2289    mPhrases[LINGER_PHRASE]->start(starttime, mSpell_elapsed); //START
2290  }
2291}
2292
2293void afxMagicSpell::leave_linger_state_c()
2294{
2295  if (mPhrases[LINGER_PHRASE])
2296  {
2297    if (mMarks_mask & MARK_INTERRUPT_LINGER)
2298    {
2299      //Con::printf("INTERRUPT LINGER (C)");
2300      mPhrases[LINGER_PHRASE]->interrupt(mSpell_elapsed);
2301    }
2302    else
2303    {
2304      //Con::printf("LEAVING LINGER (C)");
2305      mPhrases[LINGER_PHRASE]->stop(mSpell_elapsed);
2306    }
2307  }
2308}
2309
2310void afxMagicSpell::sync_client(U16 marks, U8 state, F32 elapsed, F32 spell_elapsed)
2311{
2312  //Con::printf("SYNC marks=%d old_state=%s state=%s elapsed=%g spell_elapsed=%g",
2313  //            marks, name_from_state(spell_state), name_from_state(state), elapsed,
2314  //            spell_elapsed);
2315
2316  if (mSpell_state != LATE_STATE)
2317    return;
2318
2319  mMarks_mask = marks;
2320
2321  // don't want to be started on late zoning clients
2322  if (!mDatablock->exec_on_new_clients)
2323  {
2324    mSpell_state = DONE_STATE;
2325  }
2326
2327  // it looks like we're ghosting pretty late and
2328  // should just return to the inactive state.
2329  else if ((marks & (MARK_INTERRUPTS | MARK_DEACTIVATE | MARK_SHUTDOWN)) ||
2330           (((marks & MARK_IMPACT) || (marks & MARK_END_DELIVERY)) && (marks & MARK_END_LINGER)))
2331  {
2332    mSpell_state = DONE_STATE;
2333  }
2334
2335  // it looks like we should be in the linger state.
2336  else if ((marks & MARK_IMPACT) ||
2337           (((marks & MARK_LAUNCH) || (marks & MARK_END_CASTING)) && (marks & MARK_END_DELIVERY)))
2338  {
2339    mSpell_state = LINGER_STATE;
2340    mSpell_elapsed = spell_elapsed;
2341    enter_linger_state_c(spell_elapsed-elapsed);
2342  }
2343
2344  // it looks like we should be in the delivery state.
2345  else if ((marks & MARK_LAUNCH) || (marks & MARK_END_CASTING))
2346  {
2347    mSpell_state = DELIVERY_STATE;
2348   mSpell_elapsed = spell_elapsed;
2349    enter_delivery_state_c(spell_elapsed-elapsed);
2350  }
2351
2352  // it looks like we should be in the casting state.
2353  else if (marks & MARK_ACTIVATE)
2354  {
2355    mSpell_state = CASTING_STATE; //SPELL_STATE
2356   mSpell_elapsed = spell_elapsed;
2357    enter_casting_state_c(spell_elapsed-elapsed);
2358  }
2359}
2360
2361//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
2362// public:
2363
2364void afxMagicSpell::postSpellEvent(U8 event)
2365{
2366  setMaskBits(StateEventMask);
2367
2368  switch (event)
2369  {
2370  case ACTIVATE_EVENT:
2371    mMarks_mask |= MARK_ACTIVATE;
2372    break;
2373  case LAUNCH_EVENT:
2374    mMarks_mask |= MARK_LAUNCH;
2375    setMaskBits(LaunchEventMask);
2376    break;
2377  case IMPACT_EVENT:
2378    mMarks_mask |= MARK_IMPACT;
2379    setMaskBits(ImpactEventMask);
2380    break;
2381  case SHUTDOWN_EVENT:
2382    mMarks_mask |= MARK_SHUTDOWN;
2383    break;
2384  case DEACTIVATE_EVENT:
2385    mMarks_mask |= MARK_DEACTIVATE;
2386    break;
2387  case INTERRUPT_PHASE_EVENT:
2388    if (mSpell_state == CASTING_STATE)
2389      mMarks_mask |= MARK_END_CASTING;
2390    else if (mSpell_state == DELIVERY_STATE)
2391      mMarks_mask |= MARK_END_DELIVERY;
2392    else if (mSpell_state == LINGER_STATE)
2393      mMarks_mask |= MARK_END_LINGER;
2394    break;
2395  case INTERRUPT_SPELL_EVENT:
2396    if (mSpell_state == CASTING_STATE)
2397      mMarks_mask |= MARK_INTERRUPT_CASTING;
2398    else if (mSpell_state == DELIVERY_STATE)
2399      mMarks_mask |= MARK_INTERRUPT_DELIVERY;
2400    else if (mSpell_state == LINGER_STATE)
2401      mMarks_mask |= MARK_INTERRUPT_LINGER;
2402    else if (mSpell_state == CLEANUP_STATE)
2403      mMarks_mask |= MARK_INTERRUPT_CLEANUP;
2404    break;
2405  }
2406}
2407
2408void afxMagicSpell::resolveTimeFactors()
2409{
2410  for (S32 i = 0; i < NUM_PHRASES; i++)
2411    mTfactors[i] *= mOverall_time_factor;
2412}
2413
2414//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
2415
2416void afxMagicSpell::finish_startup()
2417{
2418#if !defined(BROKEN_POINT_IN_WATER)
2419  // test if caster is in water
2420  if (mCaster)
2421  {
2422    Point3F pos = mCaster->getPosition();
2423    if (mCaster->pointInWater(pos))
2424      exec_conds_mask |= CASTER_IN_WATER;
2425  }
2426#endif
2427
2428  resolveTimeFactors();
2429
2430  init_constraints();
2431  init_scoping();
2432
2433  postSpellEvent(afxMagicSpell::ACTIVATE_EVENT);
2434}
2435
2436// static
2437afxMagicSpell*
2438afxMagicSpell::cast_spell(afxMagicSpellData* datablock, ShapeBase* caster, SceneObject* target, SimObject* extra)
2439{
2440  AssertFatal(datablock != NULL, "Datablock is missing.");
2441  AssertFatal(caster != NULL, "Caster is missing.");
2442
2443  afxMagicSpellData* exeblock = datablock;
2444
2445  SimObject* param_holder = new SimObject();
2446  if (!param_holder->registerObject())
2447  {
2448    Con::errorf("afxMagicSpell: failed to register parameter object.");
2449    delete param_holder;
2450    return 0;
2451  }
2452
2453  param_holder->assignDynamicFieldsFrom(datablock, arcaneFX::sParameterFieldPrefix);
2454  if (extra)
2455  {
2456    // copy dynamic fields from the extra object to the param holder
2457    param_holder->assignDynamicFieldsFrom(extra, arcaneFX::sParameterFieldPrefix);
2458  }
2459
2460  if (datablock->isMethod("onPreactivate"))
2461  {
2462     // CALL SCRIPT afxMagicSpellData::onPreactivate(%params, %caster, %target, %extra)
2463     bool result = datablock->onPreactivate_callback(param_holder, caster, target, extra);
2464     if (!result)
2465     {
2466   #if defined(TORQUE_DEBUG)
2467       Con::warnf("afxMagicSpell: onPreactivate() returned false, spell aborted.");
2468   #endif
2469       Sim::postEvent(param_holder, new ObjectDeleteEvent, Sim::getCurrentTime());
2470       return 0;
2471     }
2472  }
2473
2474  // make a temp datablock clone if there are substitutions
2475  if (datablock->getSubstitutionCount() > 0)
2476  {
2477    datablock = new afxMagicSpellData(*exeblock, true);
2478    exeblock->performSubstitutions(datablock, param_holder);
2479  }
2480
2481  // create a new spell instance
2482  afxMagicSpell* spell = new afxMagicSpell(caster, target);
2483  spell->setDataBlock(datablock);
2484  spell->mExeblock = exeblock;
2485  spell->setExtra(extra);
2486
2487  // copy dynamic fields from the param holder to the spell
2488  spell->assignDynamicFieldsFrom(param_holder, arcaneFX::sParameterFieldPrefix);
2489  Sim::postEvent(param_holder, new ObjectDeleteEvent, Sim::getCurrentTime());
2490
2491  // register
2492  if (!spell->registerObject())
2493  {
2494    Con::errorf("afxMagicSpell: failed to register spell instance.");
2495    Sim::postEvent(spell, new ObjectDeleteEvent, Sim::getCurrentTime());
2496    return 0;
2497  }
2498  registerForCleanup(spell);
2499
2500  spell->activate();
2501
2502  return spell;
2503}
2504
2505IMPLEMENT_GLOBAL_CALLBACK( DisplayScreenMessage, void, (GameConnection* client, const char* message), (client, message),
2506   "Called to display a screen message.\n"
2507   "@ingroup AFX\n" );
2508
2509void afxMagicSpell::displayScreenMessage(ShapeBase* caster, const char* msg)
2510{
2511  if (!caster)
2512    return;
2513
2514  GameConnection* client = caster->getControllingClient();
2515  if (client)
2516    DisplayScreenMessage_callback(client, msg);
2517}
2518
2519Point3F afxMagicSpell::getShapeImpactPos(SceneObject* obj)
2520{
2521  Point3F pos = obj->getRenderPosition();
2522  if (obj->getTypeMask() & CorpseObjectType)
2523    pos.z += 0.5f;
2524  else
2525    pos.z += (obj->getObjBox().len_z()/2);
2526  return pos;
2527}
2528
2529void afxMagicSpell::restoreObject(SceneObject* obj)
2530{
2531  if (obj->getScopeId() == mCaster_scope_id && dynamic_cast<ShapeBase*>(obj) != NULL)
2532  {
2533    mCaster_scope_id = 0;
2534   mCaster = (ShapeBase*)obj;
2535   mCaster_field = mCaster;
2536    deleteNotify(mCaster);
2537    processAfter(mCaster);
2538  }
2539
2540  if (obj->getScopeId() == mTarget_scope_id)
2541  {
2542    mTarget_scope_id = 0;
2543   mTarget = obj;
2544    mTarget_field = mTarget;
2545    deleteNotify(mTarget);
2546  }
2547
2548  if (obj->getScopeId() == mImpacted_scope_id)
2549  {
2550    mImpacted_scope_id = 0;
2551   mImpacted_obj = obj;
2552    deleteNotify(mImpacted_obj);
2553  }
2554}
2555
2556bool afxMagicSpell::activationCallInit(bool postponed)
2557{
2558  if (postponed && (!started_with_newop || !postpone_activation))
2559  {
2560    Con::errorf("afxMagicSpell::activate() -- activate() is only required when creating a spell with the \"new\" operator "
2561                "and the postponeActivation field is set to \"true\".");
2562    return false;
2563  }
2564
2565  if (!mCaster_field)
2566  {
2567    Con::errorf("afxMagicSpell::activate() -- no spellcaster specified.");
2568    return false;
2569  }
2570
2571  mCaster = dynamic_cast<ShapeBase*>(mCaster_field);
2572  if (!mCaster)
2573  {
2574    Con::errorf("afxMagicSpell::activate() -- spellcaster is not a ShapeBase derived object.");
2575    return false;
2576  }
2577
2578  if (mTarget_field)
2579  {
2580    mTarget = dynamic_cast<SceneObject*>(mTarget_field);
2581    if (!mTarget)
2582      Con::warnf("afxMagicSpell::activate() -- target is not a SceneObject derived object.");
2583  }
2584
2585  return true;
2586}
2587
2588void afxMagicSpell::activate()
2589{
2590  // separating the final part of startup allows the calling script
2591  // to make certain types of calls on the returned spell that need
2592  // to happen prior to object registration.
2593  Sim::postEvent(this, new SpellFinishStartupEvent, Sim::getCurrentTime());
2594
2595  mCaster_field = mCaster;
2596  mTarget_field = mTarget;
2597
2598  // CALL SCRIPT afxMagicSpellData::onActivate(%spell, %caster, %target)
2599  mDatablock->onActivate_callback(this, mCaster, mTarget);
2600}
2601
2602//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
2603// console methods/functions
2604
2605DefineEngineMethod(afxMagicSpell, getCaster, S32, (),,
2606                   "Returns ID of the spell's caster object.\n\n"
2607                   "@ingroup AFX")
2608{
2609  ShapeBase* caster = object->getCaster();
2610  return (caster) ? caster->getId() : -1;
2611}
2612
2613DefineEngineMethod(afxMagicSpell, getTarget, S32, (),,
2614                   "Returns ID of the spell's target object.\n\n"
2615                   "@ingroup AFX")
2616{
2617  SceneObject* target = object->getTarget();
2618  return (target) ? target->getId() : -1;
2619}
2620
2621DefineEngineMethod(afxMagicSpell, getMissile, S32, (),,
2622                   "Returns ID of the spell's magic-missile object.\n\n"
2623                   "@ingroup AFX")
2624{
2625  afxMagicMissile* missile = object->getMissile();
2626  return (missile) ? missile->getId() : -1;
2627}
2628
2629DefineEngineMethod(afxMagicSpell, getImpactedObject, S32, (),,
2630                   "Returns ID of impacted-object for the spell.\n\n"
2631                   "@ingroup AFX")
2632{
2633  SceneObject* imp_obj = object->getImpactedObject();
2634  return (imp_obj) ? imp_obj->getId() : -1;
2635}
2636
2637DefineEngineStringlyVariadicMethod(afxMagicSpell, setTimeFactor, void, 3, 4, "(F32 factor) or (string phase, F32 factor)"
2638   "Sets the time-factor for the spell, either overall or for a specific phrase.\n\n"
2639   "@ingroup AFX")
2640{
2641   if (argc == 3)
2642      object->setTimeFactor(dAtof(argv[2]));
2643   else
2644   {
2645      if (dStricmp(argv[2], "overall") == 0)
2646         object->setTimeFactor(dAtof(argv[3]));
2647      else if (dStricmp(argv[2], "casting") == 0)
2648         object->setTimeFactor(afxMagicSpell::CASTING_PHRASE, dAtof(argv[3]));
2649      else if (dStricmp(argv[2], "launch") == 0)
2650         object->setTimeFactor(afxMagicSpell::LAUNCH_PHRASE, dAtof(argv[3]));
2651      else if (dStricmp(argv[2], "delivery") == 0)
2652         object->setTimeFactor(afxMagicSpell::DELIVERY_PHRASE, dAtof(argv[3]));
2653      else if (dStricmp(argv[2], "impact") == 0)
2654         object->setTimeFactor(afxMagicSpell::IMPACT_PHRASE, dAtof(argv[3]));
2655      else if (dStricmp(argv[2], "linger") == 0)
2656         object->setTimeFactor(afxMagicSpell::LINGER_PHRASE, dAtof(argv[3]));
2657      else
2658         Con::errorf("afxMagicSpell::setTimeFactor() -- unknown spell phrase [%s].", argv[2].getStringValue());
2659   }
2660}
2661
2662DefineEngineMethod(afxMagicSpell, interruptStage, void, (),,
2663                   "Interrupts the current stage of a magic spell causing it to move onto the next one.\n\n"
2664                   "@ingroup AFX")
2665{
2666  object->postSpellEvent(afxMagicSpell::INTERRUPT_PHASE_EVENT);
2667}
2668
2669DefineEngineMethod(afxMagicSpell, interrupt, void, (),,
2670                   "Interrupts and deletes a running magic spell.\n\n"
2671                   "@ingroup AFX")
2672{
2673  object->postSpellEvent(afxMagicSpell::INTERRUPT_SPELL_EVENT);
2674}
2675
2676DefineEngineMethod(afxMagicSpell, activate, void, (),,
2677                   "Activates a magic spell that was started with postponeActivation=true.\n\n"
2678                   "@ingroup AFX")
2679{
2680  if (object->activationCallInit(true))
2681    object->activate();
2682}
2683
2684DefineEngineFunction(castSpell, S32,   (afxMagicSpellData* datablock, ShapeBase* caster, SceneObject* target, SimObject* extra),
2685                              (nullAsType<afxMagicSpellData*>(), nullAsType<ShapeBase*>(), nullAsType<SceneObject*>(), nullAsType<SimObject*>()),     
2686                     "Instantiates the magic spell defined by datablock and cast by caster.\n\n"
2687                     "@ingroup AFX")
2688{
2689  if (!datablock)
2690  {
2691    Con::errorf("castSpell() -- missing valid spell datablock.");
2692    return 0;
2693  }
2694
2695  if (!caster)
2696  {
2697    Con::errorf("castSpell() -- missing valid spellcaster.");
2698    return 0;
2699  }
2700
2701  // target is optional (depends on spell)
2702
2703  // note -- we must examine all arguments prior to calling cast_spell because
2704  // it calls Con::executef() which will overwrite the argument array.
2705  afxMagicSpell* spell = afxMagicSpell::cast_spell(datablock, caster, target, extra);
2706
2707  return (spell) ? spell->getId() : 0;
2708}
2709
2710//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
2711