afxMagicSpell.cpp
Engine/source/afx/afxMagicSpell.cpp
Classes:
class
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 , 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_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