public CastRoutine SpellRangedAttack(SpellAttackEffector hit,
                                         MissileScript.MissileAnimationName missileName,
                                         SpellAttackEffector crit = null,
                                         SpellAttackEffector miss = null)
    {
        return((AT.Battle.Actor caster,AT.Battle.Action cast,List <AT.ATTile> targets) => {
            GameObject prefab = MissileAnimationPrefabDispenser.instance.GetAnimationPrefabByName(missileName);

            foreach (AT.ATTile target in targets)
            {
                AttackSituation sit = new AttackSituation(caster.CharSheet,target.FirstOccupant.CharSheet,cast);

                List <AT.Character.Effect.GenericEffect> resultingEffects = null;

                ResultType result = sit.GetResult();
                switch (result)
                {
                case ResultType.HIT:
                    resultingEffects = hit.Invoke(cast,target);
                    break;

                case ResultType.CRITICAL_HIT:
                    if (crit != null)
                    {
                        resultingEffects = crit.Invoke(cast,target);
                    }
                    break;

                case ResultType.MISS:
                    if (miss != null)
                    {
                        resultingEffects = miss.Invoke(cast,target);
                    }
                    break;

                case ResultType.CRITICAL_MISS:
                    //nothing
                    break;
                }


                CharacterCastingAnimation castingAnims = caster.GetComponent <CharacterCastingAnimation>();
                if (castingAnims != null)
                {
                    castingAnims.SetupAndDoCastAnimation(cast);
                    castingAnims.OneShotSpellRelease((animationInst) => {
                        GameObject copy = Instantiate(prefab);
                        copy.transform.position = caster.transform.position;
                        MissileScript missile = copy.GetComponent <MissileScript>();

                        missile.LaunchAt(target,true);
                        //Safe to not worry about unsubbing, thanks to garbage cleanup
                        missile.OnConnectedWithTarget += (MissileScript self) => {
                            if (resultingEffects != null)
                            {
                                foreach (AT.Character.Effect.GenericEffect effect in resultingEffects)
                                {
                                    if (target.FirstOccupant != null)
                                    {
                                        effect.ApplyTo(target.FirstOccupant.CharSheet,cast);
                                    }
                                }
                            }
                            //This assumes only one missile.   Needs a "Checkin of sorts, to conditionally check on finished"
                            cast.CallOnFinished();
                        };
                    });                     //ToBe ATOCIJL
                }
                else
                {
                    GameObject copy = Instantiate(prefab);
                    copy.transform.position = caster.transform.position;
                    MissileScript missile = copy.GetComponent <MissileScript>();
                    missile.LaunchAt(target.transform);
                    //Safe to not worry about unsubbing, thanks to garbage cleanup
                    missile.OnConnectedWithTarget += (MissileScript self) => {
                        if (resultingEffects != null)
                        {
                            foreach (AT.Character.Effect.GenericEffect effect in resultingEffects)
                            {
                                effect.ApplyTo(target.FirstOccupant.CharSheet,cast);
                            }
                        }
                        //This assumes only one missile.   Needs a "Checkin of sorts, to conditionally check on finished"
                        cast.CallOnFinished();
                    };
                }
            }
        });
    }
            public override void Perform()
            {
                CallOnBegan();

                //TargetEnemyTile param = (TargetEnemyTile) parameters [0];
                //IsOffhand opt = (IsOffhand) ActionOptions [0].OptVal;

                AT.Character.Effect.PhysicalDamage hit;
                AT.Character.Effect.PhysicalDamage crit;

                Actor target = TargetActor();

                attackSituation = new AttackSituation(actor.CharSheet, target.CharSheet, this);

                ResultType result = attackSituation.GetResult();

                resultingEffect = null;


                if (IsOffhand)
                {
                    hit  = actor.CharSheet.OffhandAttackDamageEffect(attackSituation);
                    crit = actor.CharSheet.OffhandAttackCritDamageEffect(attackSituation);
                    //IsOffhand = true;
                }
                else
                {
                    hit  = actor.CharSheet.MainHandAttackDamageEffect(attackSituation);
                    crit = actor.CharSheet.MainHandAttackCritDamageEffect(attackSituation);
                }

                switch (result)
                {
                case ResultType.CRITICAL_HIT:
                    resultingEffect = crit;
                    break;

                case ResultType.HIT:
                    resultingEffect = hit;
                    break;

                case ResultType.MISS:
                    //	actor.OnMissedAttack ();
                    break;

                case ResultType.CRITICAL_MISS:
                    //	actor.OnMissedAttack ();
                    break;
                }
                //			Debug.Log("Attack was made: " + attackSituation.VerboseToString());



                if (actor.GetComponent <CharacterAttacker> () != null)
                {
                    actor.GetComponent <CharacterAttacker> ().OnAttackAnimationEnded += WaitForAnimationFinish;
                }
                else
                {
                    if (resultingEffect != null)
                    {
                        resultingEffect.ApplyTo(target.CharSheet, this);
                    }
                    else
                    {
                        //target.InstantiateMissText ();
                    }
                    CallOnFinished();
                }
            }
    public CastRoutine SpellTouchAttack(SpellAttackEffector hit,SpellAttackEffector crit = null,SpellAttackEffector miss = null)
    {
        return((AT.Battle.Actor caster,AT.Battle.Action cast,List <AT.ATTile> targets) => {
            foreach (AT.ATTile target in targets)
            {
                AttackSituation sit = new AttackSituation(caster.CharSheet,target.FirstOccupant.CharSheet,cast);

                List <AT.Character.Effect.GenericEffect> resultingEffects = null;

                ResultType result = sit.GetResult();
                switch (result)
                {
                case ResultType.HIT:
                    resultingEffects = hit.Invoke(cast,target);
                    break;

                case ResultType.CRITICAL_HIT:
                    if (crit != null)
                    {
                        resultingEffects = crit.Invoke(cast,target);
                    }
                    break;

                case ResultType.MISS:
                    if (miss != null)
                    {
                        resultingEffects = miss.Invoke(cast,target);
                    }
                    break;

                case ResultType.CRITICAL_MISS:
                    //nothing
                    break;
                }


                CharacterCastingAnimation castingAnims = caster.GetComponent <CharacterCastingAnimation>();
                if (castingAnims != null)
                {
                    castingAnims.SetupAndDoCastAnimation(cast);
                    castingAnims.OneShotSpellRelease((animationInst) => {
                        if (resultingEffects != null)
                        {
                            foreach (AT.Character.Effect.GenericEffect effect in resultingEffects)
                            {
                                if (target.FirstOccupant != null)
                                {
                                    effect.ApplyTo(target.FirstOccupant.CharSheet,cast);
                                }
                            }
                        }
                    });

                    castingAnims.OneShotAnimEnded((animationInst) => {
                        cast.CallOnFinished();
                    });
                }
                else
                {
                    if (resultingEffects != null)
                    {
                        foreach (AT.Character.Effect.GenericEffect effect in resultingEffects)
                        {
                            effect.ApplyTo(target.FirstOccupant.CharSheet,cast);
                        }
                    }
                    cast.CallOnFinished();
                }
            }
        });
    }