예제 #1
0
        public SpellTarget FindBestUsage(PlayedFighter pc, Spell.SpellCategory category, bool withWeapon, IEnumerable <Cell> possiblePlacement = null)
        {
            SpellTarget spellTarget = new SpellTarget();

            if (pc.PCStats.CurrentAP <= 0)
            {
                return(spellTarget);
            }
            Stopwatch timer = new Stopwatch();

            timer.Start();
            PathFinder         pathFinder  = new PathFinder(pc.Fight, true);
            IEnumerable <Cell> sourceCells = possiblePlacement == null?pc.GetPossibleMoves(true, true, pathFinder) : possiblePlacement;

            IEnumerable <Fighter> actorsWithoutPC = pc.Fight.AliveActors.Where(actor => actor != pc);
            List <Spell>          spells          = m_spells.ToList();

            if (withWeapon && ((category & (Spell.SpellCategory.Damages | Spell.SpellCategory.Healing)) != 0))
            {
                spells.Add(WeaponSpellLike());
            }
            //logger.Debug("***FindBestUsage {0}, {1} spells in book. {2} PA. {3}/{4} HP ***", category, spells.Count, pc.PCStats.CurrentAP, pc.PCStats.Health, pc.PCStats.MaxHealth);
            Object thisLock = new Object();
            //foreach(Spell spell in spells)
            int NbSpellsChecked = 0;

            Parallel.ForEach(spells, spell =>
            {
                NbSpellsChecked++;
                if (spell != null && !IgnoredSpells.Contains(spell.Template.id))
                {
                    int spellId = spell.Template.id;
                    if (spell.IsAvailable(null) && ((spellId != 0 && pc.CanCastSpells) || (spellId == 0 && pc.CanFight)) && spell.LevelTemplate.apCost <= pc.Stats.CurrentAP && pc.CanCastSpell(spellId))
                    {
                        if ((spell.Categories & category) > 0)
                        {
                            SpellTarget lastSpellTarget = spell.FindBestTarget(pc, sourceCells, actorsWithoutPC, category);
                            if (lastSpellTarget != null && lastSpellTarget.Efficiency > spellTarget.Efficiency)
                            {
                                //lock (thisLock)
                                spellTarget = lastSpellTarget;
                            }
                            //if (lastSpellTarget != null)
                            //    logger.Debug("efficiency {0} = {1} ({2})", lastSpellTarget.Spell, lastSpellTarget.Efficiency, lastSpellTarget.Comment);
                            //lock (thisLock)
//                                    logger.Debug("efficiency {0} = ???", spell);
//                            else
                            //lock (thisLock)
                            //                                  logger.Debug("efficiency {0} = {1} ({2})", lastSpellTarget.Spell, lastSpellTarget.Efficiency, lastSpellTarget.Comment);
                        }
                    }
                    else
                    {
                        //                      lock (thisLock)
                        //                          logger.Info("{0} skipped : available={1} ({6}), canUse={2}, ApCost={3}, CanCast({4})={5}", spell, spell.IsAvailable(null), ((spellId != 0 && pc.CanCastSpells) || (spellId == 0 && pc.CanFight)), spell.LevelTemplate.apCost <= pc.Stats.CurrentAP, spellId, pc.CanCastSpell(spellId), spell.AvailabilityExplainString(null));
                    }
                }
            }
                             );
            //Debug.Assert(NbSpellsChecked == spells.Count);
            timer.Stop();
            spellTarget.TimeSpan = timer.Elapsed;
            //pc.Character.SendInformation("Spell {0} selected - efficientcy : {1} - comment = {2}", spellTarget.Spell, spellTarget.Efficiency, spellTarget.Comment);
            return(spellTarget);
        }
예제 #2
0
        // Precise compute efficiency of a spell for a given category (beware to remove pc from friend and friendcells lists before calling this !)
        // Returns -1 if it would hit friends (todo : optimize if needed)
        public int GetFullAreaEffect(PlayedFighter pc, Cell source, Cell dest, IEnumerable <Fighter> actors, Spell.SpellCategory category, ref string comment)
        {
            SpellImpact spellImpact = new SpellImpact();

            foreach (EffectInstanceDice effect in LevelTemplate.effects)
            {
                if ((Spell.GetEffectCategories((uint)effect.effectId, LevelTemplate.id) & category) > 0)
                {
                    comment += " Effect " + (EffectsEnum)(effect.effectId) + " : ";
                    EffectDice         effectCl   = new EffectDice(effect);
                    IEnumerable <Cell> cells      = effectCl.GetArea(source, dest);
                    SpellTargetType    targetType = (SpellTargetType)effect.targetId;
                    int nbAffectedTargets         = 0;
                    if (EffectBase.canAffectTarget(effectCl, this, pc, pc) && cells.Contains(source))
                    {
                        // Caster would be affected
                        DamageType efficiency = Spell.CumulEffects(effect, ref spellImpact, pc, pc /*, category*/, this);
                        if (efficiency != 0)
                        {
                            comment += string.Format("{0} on {1} => {2}, ", (decimal)efficiency, pc, (decimal)spellImpact.Damage);
                        }
                        nbAffectedTargets++;
                        if (efficiency < 0)
                        {
                            return(0);                // The caster would be affected by a bad spell => give up
                        }
                    }

                    foreach (var actor in actors.Where(act => cells.Contains(act.Cell))) // All actors within the area covered by the spell
                    {
                        if (!EffectBase.canAffectTarget(effectCl, this, pc, actor))
                        {
                            continue;                                                         // This actor is not affected by the spell
                        }
                        DamageType damage = Spell.CumulEffects(effect, ref spellImpact, pc, actor /*, category*/, this);
                        if (damage != 0)
                        {
                            comment += string.Format(" - {0} on {1} => {2}", (decimal)damage, actor, (decimal)spellImpact.Damage);
                        }

                        nbAffectedTargets++;
                        //if (damage > 0 && actor.Team == pc.Team) return 0; // Harmful on a friend => give up
                    }

                    //if (nbAffectedTargets > 1)
                    //{
                    //    pc.Character.SendWarning("Spell {0} : {1} targets affected for {2} damage - {3}", this, nbAffectedTargets, spellImpact.Damage, comment);
                    //}
                }
            }

            if (Template.id == 139) // Mot d'altruisme : only use near end of fight or if lot of spellImpact to heal
            {
                int hpLeftOnFoes = actors.Where(actor => actor.Team.Id != pc.Team.Id).Sum(actor => actor.Stats.Health);
                comment += string.Format(" - special \"Mot d'altruisme\" processing : hpLeftOnFoes = {0}, efficiency = {1}", hpLeftOnFoes, (int)spellImpact.Damage);
                if (hpLeftOnFoes > 500) // Not the end of the fight
                {
                    if (spellImpact.Damage < 300)
                    {
                        return(0);                          // Do not cast it if less than 300 hp of healing
                    }
                    else
                    {
                        return((int)spellImpact.Damage / 3); // Otherwise, far from the end of the fight, divide efficiency by 3
                    }
                }
                // if close to the end of the fight, then returns full result.
            }

            return((int)spellImpact.Damage);
        }
예제 #3
0
        // Find the best cell to cast a given spell, retrieve damage done and best cell (todo : optimize if needed)
        public SpellTarget FindBestTarget(PlayedFighter pc, Cell source, IEnumerable <Cell> destCells, IEnumerable <Fighter> actors, Spell.SpellCategory category)
        {
            SpellTarget result = null;

            foreach (Cell dest in destCells)
            {
                string comment     = string.Empty;
                int    efficientcy = 0;
                if (areaDependsOnDirection || !efficiencyCache.TryGetValue(dest.Id, out efficientcy))
                {
                    efficientcy = GetFullAreaEffect(pc, source, dest, actors, category, ref comment);
                    if (!areaDependsOnDirection)
                    {
                        efficiencyCache[dest.Id] = efficientcy;
                    }
                }
                if (efficientcy > 0)
                {
                    if (result == null)
                    {
                        result         = new SpellTarget(efficientcy, source, dest, this);
                        result.Comment = comment;
                    }
                    else
                    if (efficientcy > result.Efficiency)
                    {
                        result.Efficiency = efficientcy;
                        result.FromCell   = source;
                        result.TargetCell = dest;
                        result.Spell      = this;
                        result.Comment    = comment;
                    }
                }
            }
            return(result);
        }
예제 #4
0
        // Find where the PC should come to cast the spell, and the best target there (todo : optimize if needed)
        public SpellTarget FindBestTarget(PlayedFighter pc, IEnumerable <Cell> sourceCells, IEnumerable <Fighter> actors, Spell.SpellCategory category)
        {
            if (LevelTemplate.statesForbidden != null)
            {
                if (LevelTemplate.statesForbidden.Any(state => pc.HasState(state)))
                {
                    logger.Debug("Spell {0} skipped : statesForbidden {1}", this, string.Join(",", LevelTemplate.statesForbidden));
                    pc.Character.SendWarning("Spell {0} skipped : statesForbidden {1}", this, string.Join(",", LevelTemplate.statesForbidden));
                    return(null); // Can't cast this : all the required states are not on the caster
                }
            }
            if (LevelTemplate.statesRequired != null)
            {
                if (!LevelTemplate.statesRequired.All(state => pc.HasState(state)))
                {
                    logger.Debug("Spell {0} skipped : statesRequired {1}", this, string.Join(",", LevelTemplate.statesForbidden));
                    pc.Character.SendWarning("Spell {0} skipped : statesRequired {1}", this, string.Join(",", LevelTemplate.statesForbidden));
                    return(null); // Can't cast this : at least one required state is not on the caster
                }
            }
            if (IsMaitrise(null)) // If the spell is a maitrise, then ignore it if not of the proper type for equipped weapon.
            {
                Weapon weapon = pc.Character.Inventory.GetEquippedWeapon();
                if (weapon == null)
                {
                    return(null);
                }
                if (!IsMaitrise(weapon.typeId))
                {
                    return(null);
                }
            }
            _losMap = new LOSMap(pc.Fight);
            SpellTarget bestResult = null;

            #region optimisations
            IEnumerable <Fighter> enemies = pc.GetOpposedTeam().FightersAlive;
            IEnumerable <Fighter> friends = pc.Team.FightersAlive;

            bool goodSpell = (Categories & (SpellCategory.Buff | SpellCategory.Healing)) != 0;
            bool badSpell  = (Categories & (SpellCategory.Damages | SpellCategory.Curse)) != 0;
            IEnumerable <Fighter> targets = null;
            int targetsCount = 0;
            if (goodSpell && badSpell)
            {
                goodSpell = badSpell = false;
            }
            else
            {
                targets      = goodSpell ? friends : enemies;
                targetsCount = targets.Count();
            }
            uint surface = this.GetArea(category);
            efficiencyCache = null;
            if (!areaDependsOnDirection)
            {
                efficiencyCache = new Dictionary <short, int>();
            }
            #endregion
            if (surface == 1 && LevelTemplate.range == 0) // Hack fast Cure and protect self
            {
                var res = GetSpellDamages(pc, pc);
                if (res.Damage > 0)
                {
                    bestResult = new SpellTarget(res.Damage, pc.Cell, pc.Cell, this);
                }
            }
            else
            {
                foreach (Cell source in sourceCells)
                {
                    IEnumerable <Cell> destCells = GetCellsAtSpellRange(pc, source, actors);
                    if (goodSpell || badSpell)
                    {
                        if (surface <= 1 && LevelTemplate.range > 0)
                        {
                            destCells = destCells.Intersect(targets.Select(fighter => fighter.Cell)); // for spells that have an area of effect of 1, just find enemies or friends as targets. No need to scan all the range.
                        }
                    }
                    if (surface >= 560 && destCells.Count() > 1) // For spells that cover the full map, use only the first cell
                    {
                        destCells = destCells.Take(1);
                    }
                    SpellTarget newResult = FindBestTarget(pc, source, destCells, actors, category);
                    if (newResult == null || newResult.Efficiency <= 0)
                    {
                        continue;
                    }
                    if (bestResult == null || bestResult.Efficiency < newResult.Efficiency)
                    {
                        bestResult = newResult;
                        if (surface >= 560)
                        {
                            break;                 // if spell covers all map, and we have some hit, then no need to continue (first source cells are nearest)
                        }
                        if (targetsCount == 1 && surface == 1)
                        {
                            break;                                    // only one target and 1 cell area spell => no need to loop further
                        }
                    }
                }
            }
            if (bestResult != null)
            {
                bestResult.Efficiency *= (pc.Stats.CurrentAP / LevelTemplate.apCost);
                bestResult.cast        = false;
            }

            return(bestResult);
        }