// 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; }
public DamageType GetTotalDamageOnAllEnemies(PlayedFighter caster) { DamageType damages = 0; foreach (Fighter enemy in caster.Team == null ? caster.Fight.AliveActors : caster.GetOpposedTeam().FightersAlive) { SpellImpact impact = null; foreach (var effect in LevelTemplate.effects) damages += CumulEffects(effect, ref impact, caster, enemy/*, Spell.SpellCategory.Damages*/, this); } return damages; }